Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TestCafe wait for results #878

Merged
merged 11 commits into from
Jul 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion tests/acceptance/blocks/thememap.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ class ThemeMap {

async dragLeft () {
await t.drag(this._canvas, 750, 0);
await t.wait(1000); // Wait longer than the debouncer to allow a new search to be triggered
}

async toggleSearchThisArea () {
Expand Down
4 changes: 2 additions & 2 deletions tests/acceptance/blocks/verticalresults.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Selector, t } from 'testcafe';
import { Selector, RequestLogger, t } from 'testcafe';

/**
* Models the user interaction with a {import('@yext/answers-search-ui').VerticalResultsComponent}.
*/
class VerticalResults {
constructor () {
this._selector = Selector('.yxt-Results')
this._selector = Selector('.yxt-Results');
this._resultsWrapper = Selector('.Answers-resultsWrapper');
this._focusedCard = Selector('.yxt-Card--pinFocused');
this._getNthCard = index => Selector(`.yxt-Card[data-opts*="${index}"]`);
Expand Down
4 changes: 3 additions & 1 deletion tests/acceptance/constants.js
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
module.exports.PORT=9999;
module.exports.PORT=9999;
module.exports.VERTICAL_SEARCH_URL_REGEX=/v2\/accounts\/me\/answers\/vertical\/query/;
module.exports.UNIVERSAL_SEARCH_URL_REGEX=/v2\/accounts\/me\/answers\/query/;
62 changes: 62 additions & 0 deletions tests/acceptance/searchrequestlogger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { VERTICAL_SEARCH_URL_REGEX, UNIVERSAL_SEARCH_URL_REGEX } from './constants';
import { RequestLogger, t } from 'testcafe';
import { registerIE11NoCacheHook } from '../test-utils/testcafe';

/**
* Handles request logger registration and request/response data received during test execution.
*/
class SearchRequestLogger {

/**
* Register a RequestLogger that tracks vertical query requests to given test.
* If browser is IE11, register an Ie11NoCacheHook.
*
* @param {import('testcafe').TestController} testInstance
*/
async registerVerticalSearchLogger(testInstance) {
this._queryRequestLogger = RequestLogger({
url: VERTICAL_SEARCH_URL_REGEX
});
await testInstance.addRequestHooks(this._queryRequestLogger);
await registerIE11NoCacheHook(testInstance, VERTICAL_SEARCH_URL_REGEX);
}

/**
* Register a RequestLogger that tracks universal query requests to given test.
* If browser is IE11, register an Ie11NoCacheHook.
*
* @param {import('testcafe').TestController} testInstance
*/
async registerUniversalSearchLogger(testInstance) {
this._queryRequestLogger = RequestLogger({
url: UNIVERSAL_SEARCH_URL_REGEX
});
await testInstance.addRequestHooks(this._queryRequestLogger);
await registerIE11NoCacheHook(testInstance, UNIVERSAL_SEARCH_URL_REGEX);
}

/**
* Wait for results to load on page by checking query response status
* (timeout is set to 10 seconds)
*/
async waitOnSearchComplete() {
const responseWaitTimeout = 10000;
const waitTimeInterval = 200;
let totalWaitTime = 0;
while (totalWaitTime < responseWaitTimeout && !this.isLoggerResultsPresent()) {
await t.wait(waitTimeInterval);
totalWaitTime += waitTimeInterval;
}
this._queryRequestLogger.clear();
}

/**
* Returns true if there exists a query response from logger with status code 200
* @returns {boolean}
*/
async isLoggerResultsPresent() {
return await this._queryRequestLogger.contains(r => r.response.statusCode === 200);
}
}

export default new SearchRequestLogger();
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ import SearchBar from '../blocks/searchbar';
import VerticalResults from '../blocks/verticalresults';
import ThemeMap from '../blocks/thememap';
import CollapsibleFilters from '../blocks/collapsiblefilters';
import SearchRequestLogger from '../searchrequestlogger';

fixture`Vertical Full Page Map with Filters and Clusters`
.page(`http://localhost:${PORT}/locations_full_page_map_with_filters`)
.beforeEach(async t => {
await SearchRequestLogger.registerVerticalSearchLogger(t);
});

test('Clicking on a pin closes the filter view', async t => {
await SearchBar.submitQuery('virginia');
await SearchRequestLogger.waitOnSearchComplete();
await CollapsibleFilters.viewFilters();
await ThemeMap.selectPin();
const isFilterViewOpen = await CollapsibleFilters.isFilterViewOpen();
Expand All @@ -17,6 +22,7 @@ test('Clicking on a pin closes the filter view', async t => {

test('Clicking on a cluster causes the map to zoom in', async t => {
await SearchBar.submitQuery('virginia');
await SearchRequestLogger.waitOnSearchComplete();
const zoom = await ThemeMap.getZoom();
await ThemeMap.selectPinCluster();
const zoomAfterSelectingCluster = await ThemeMap.getZoom();
Expand All @@ -25,6 +31,7 @@ test('Clicking on a cluster causes the map to zoom in', async t => {

test('Clicking on a cluster causes a new search to be ran', async t => {
await SearchBar.submitQuery('virginia');
await SearchRequestLogger.waitOnSearchComplete();
const numResults = await VerticalResults.getNumResults();
await ThemeMap.selectPinCluster();
const numResultsAfterSelectingCluster = await VerticalResults.getNumResults();
Expand Down
16 changes: 15 additions & 1 deletion tests/acceptance/suites/vertical-full-page-map.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,24 @@ import SearchBar from '../blocks/searchbar';
import VerticalResults from '../blocks/verticalresults';
import ThemeMap from '../blocks/thememap';
import Pagination from '../blocks/pagination';
import SearchRequestLogger from '../searchrequestlogger';

fixture`Vertical Full Page Map`
.page(`http://localhost:${PORT}/locations_full_page_map`)
.beforeEach(async t => {
await SearchRequestLogger.registerVerticalSearchLogger(t);
});

test('Can search and get results', async t => {
await SearchBar.submitQuery('virginia');
await SearchRequestLogger.waitOnSearchComplete();
const isResultsPresent = await VerticalResults.isResultsPresent();
await t.expect(isResultsPresent).ok();
});

test('Clicking on a pin focuses on a results card', async t => {
await SearchBar.submitQuery('virginia');
await SearchRequestLogger.waitOnSearchComplete();
let isCardFocused = await VerticalResults.isCardFocused();
await t.expect(isCardFocused).notOk();
await ThemeMap.selectPin();
Expand All @@ -24,40 +30,48 @@ test('Clicking on a pin focuses on a results card', async t => {

test('Search when map moves works', async t => {
await SearchBar.submitQuery('virginia');
await SearchRequestLogger.waitOnSearchComplete();
const resultsCountBeforeDrag = await VerticalResults.getNumResults();
await ThemeMap.dragLeft();
await ThemeMap.dragLeft();
await SearchRequestLogger.waitOnSearchComplete();
const resultsCountAfterDrag = await VerticalResults.getNumResults();
await t.expect(resultsCountBeforeDrag !== resultsCountAfterDrag).ok();
});

test('Search this area button works', async t => {
await SearchBar.submitQuery('virginia');
await ThemeMap.toggleSearchThisArea();
await SearchRequestLogger.waitOnSearchComplete();
const resultsCountBeforeDrag = await VerticalResults.getNumResults();
await ThemeMap.dragLeft();
await ThemeMap.clickSearchThisAreaButton();
await SearchRequestLogger.waitOnSearchComplete();
const resultsCountAfterDrag = await VerticalResults.getNumResults();
await t.expect(resultsCountBeforeDrag !== resultsCountAfterDrag).ok();
});

test('Default initial search works and is enabled by default', async t => {
await SearchRequestLogger.waitOnSearchComplete();
const resultsCount = await VerticalResults.getNumResults();
await t.expect(resultsCount).ok();
});

test('Pagination works', async t => {
const initialResultsOffset = await VerticalResults.getResultsOffset();
await Pagination.nextResults();
await SearchRequestLogger.waitOnSearchComplete();
const updatedResultsOffset = await VerticalResults.getResultsOffset();
await t.expect(initialResultsOffset).notEql(updatedResultsOffset);
});

test('Pagination scrolls the results to the top', async t => {
await SearchRequestLogger.waitOnSearchComplete();
await VerticalResults.scrollToBottom();
const scrollTop = await VerticalResults.getScrollTop();
await t.expect(scrollTop).notEql(0);
await Pagination.nextResults();
await SearchRequestLogger.waitOnSearchComplete();
const scrollTopAfterPagination = await VerticalResults.getScrollTop();
await t.expect(scrollTopAfterPagination).eql(0);
});
});
31 changes: 31 additions & 0 deletions tests/test-utils/ie11nocachehook.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { RequestHook } from 'testcafe';

/**
* IE11NoCacheHook solves the problem of IE11 caching all ajax requests
* by default by adding a 'no-store' header to requests in ie11.
*
* Without this hook, ajax requests cached by IE11 will be ignored
* testcafe's RequestLogger. This is only a problem in IE11.
*/
export default class IE11NoCacheHook extends RequestHook {
/**
* This comes from the suggestion here on the testcafe github
* https://github.com/DevExpress/testcafe/issues/3780#issuecomment-496955368
* The --disable-page-caching flag did not work for ie11, and neither
* did trying to set the header in onRequest(), so this workaround was used instead.
*
* @param {Object} event
*/
_onConfigureResponse (event) {
super._onConfigureResponse(event);
event.setHeader('cache-control', 'no-store');
}

async onRequest () {
// We don't need to do anything here, but still need to override the method.
}

async onResponse () {
// We don't need to do anything here, but still need to override the method.
}
}
18 changes: 18 additions & 0 deletions tests/test-utils/testcafe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { ClientFunction } from 'testcafe';
import IE11NoCacheHook from './ie11nocachehook';

/**
* Register the Ie11NoCacheHook, if the current browser is IE11.
*
* @param {import('testcafe').TestController} testInstance
* @param {string} url
*/
export async function registerIE11NoCacheHook (testInstance, url) {
const isIE11 = await ClientFunction(() => {
return !!window.MSInputMethodContext && !!document.documentMode;
})();
if (isIE11) {
const ie11Hook = new IE11NoCacheHook(url);
return testInstance.addRequestHooks(ie11Hook);
}
}