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

Adding waitForTimeout/waitForSelector for assets discovery browser when JS is enabled #1715

Merged
merged 1 commit into from
Sep 16, 2024
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
10 changes: 10 additions & 0 deletions packages/core/src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,14 @@ export const configSchema = {
maximum: 750,
minimum: 1
},
waitForSelector: {
type: 'string'
},
waitForTimeout: {
type: 'integer',
minimum: 1,
maximum: 30000
},
disableCache: {
type: 'boolean'
},
Expand Down Expand Up @@ -295,6 +303,8 @@ export const snapshotSchema = {
allowedHostnames: { $ref: '/config/discovery#/properties/allowedHostnames' },
disallowedHostnames: { $ref: '/config/discovery#/properties/disallowedHostnames' },
requestHeaders: { $ref: '/config/discovery#/properties/requestHeaders' },
waitForSelector: { $ref: '/config/discovery#/properties/waitForSelector' },
waitForTimeout: { $ref: '/config/discovery#/properties/waitForTimeout' },
authorization: { $ref: '/config/discovery#/properties/authorization' },
disableCache: { $ref: '/config/discovery#/properties/disableCache' },
captureMockedServiceWorker: { $ref: '/config/discovery#/properties/captureMockedServiceWorker' },
Expand Down
17 changes: 16 additions & 1 deletion packages/core/src/discovery.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logger from '@percy/logger';
import Queue from './queue.js';
import Page from './page.js';
import {
normalizeURL,
hostnameMatches,
Expand All @@ -9,7 +10,9 @@ import {
createLogResource,
yieldAll,
snapshotLogName,
withRetries
waitForTimeout,
withRetries,
waitForSelectorInsideBrowser
} from './utils.js';
import {
sha256hash
Expand Down Expand Up @@ -203,6 +206,18 @@ async function* captureSnapshotResources(page, snapshot, options) {
yield resizePage(snapshot.widths[0]);
yield page.goto(snapshot.url, { cookies });

// wait for any specified timeout
if (snapshot.discovery.waitForTimeout && page.enableJavaScript) {
log.debug(`Wait for ${snapshot.discovery.waitForTimeout}ms timeout`);
await waitForTimeout(snapshot.discovery.waitForTimeout);
}

// wait for any specified selector
if (snapshot.discovery.waitForSelector && page.enableJavaScript) {
log.debug(`Wait for selector: ${snapshot.discovery.waitForSelector}`);
await waitForSelectorInsideBrowser(page, snapshot.discovery.waitForSelector, Page.TIMEOUT);
}

if (snapshot.execute) {
// when any execute options are provided, inject snapshot options
/* istanbul ignore next: cannot detect coverage of injected code */
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/snapshot.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ function getSnapshotOptions(options, { config, meta }) {
allowedHostnames: config.discovery.allowedHostnames,
disallowedHostnames: config.discovery.disallowedHostnames,
networkIdleTimeout: config.discovery.networkIdleTimeout,
waitForTimeout: config.discovery.waitForTimeout,
waitForSelector: config.discovery.waitForSelector,
devicePixelRatio: config.discovery.devicePixelRatio,
requestHeaders: config.discovery.requestHeaders,
authorization: config.discovery.authorization,
Expand Down
9 changes: 9 additions & 0 deletions packages/core/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,15 @@ async function waitForSelector(selector, timeout) {
}
}

// wait for a query selector to exist within an optional timeout inside browser
export async function waitForSelectorInsideBrowser(page, selector, timeout) {
try {
return page.eval(`await waitForSelector(${JSON.stringify(selector)}, ${timeout})`);
} catch {
throw new Error(`Unable to find: ${selector}`);
}
}

// Browser-specific util to wait for an xpath selector to exist within an optional timeout.
/* istanbul ignore next: tested, but coverage is stripped */
async function waitForXPath(selector, timeout) {
Expand Down
71 changes: 71 additions & 0 deletions packages/core/test/discovery.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2430,6 +2430,77 @@ describe('Discovery', () => {
});
});

describe('waitForSelector/waitForTimeout at the time of discovery when Js is enabled =>', () => {
it('calls waitForTimeout, waitForSelector and page.eval when their respective arguments are given', async () => {
const page = await percy.browser.page();
spyOn(percy.browser, 'page').and.returnValue(page);
spyOn(page, 'eval').and.callThrough();
percy.loglevel('debug');

await percy.snapshot({
name: 'test discovery',
url: 'http://localhost:8000',
enableJavaScript: true,
discovery: {
waitForTimeout: 100,
waitForSelector: 'body'
}
});
await percy.idle();

expect(page.eval).toHaveBeenCalledWith('await waitForSelector("body", 30000)');
expect(logger.stderr).toEqual(jasmine.arrayContaining([
'[percy:core:discovery] Wait for selector: body',
'[percy:core:discovery] Wait for 100ms timeout'
]));
});
});
describe('waitForSelector/waitForTimeout at the time of discovery when Js is disabled =>', () => {
it('donot calls waitForTimeout, waitForSelector and page.eval', async () => {
const page2 = await percy.browser.page();
spyOn(page2, 'eval').and.callThrough();
percy.loglevel('debug');

await percy.snapshot({
name: 'test discovery 2',
url: 'http://localhost:8000',
cliEnableJavaScript: false,
discovery: {
waitForTimeout: 100,
waitForSelector: 'flex'
}
});

await percy.idle();

expect(page2.eval).not.toHaveBeenCalledWith('await waitForSelector("flex", 30000)');
expect(logger.stderr).not.toEqual(jasmine.arrayContaining([
'[percy:core:discovery] Wait for 100ms timeout',
'[percy:core:discovery] Wait for selector: flex'
]));
});
it('when domSnapshot is present with default parameters waitForSelector/waitForTimeout are not called', async () => {
percy.loglevel('debug');

await percy.snapshot({
name: 'test discovery 3',
url: 'http://localhost:8000',
domSnapshot: testDOM,
discovery: {
waitForTimeout: 100,
waitForSelector: 'body'
}
});

await percy.idle();

expect(logger.stderr).not.toEqual(jasmine.arrayContaining([
'[percy:core:discovery] Wait for 100ms timeout',
'[percy:core:discovery] Wait for selector: body'
]));
});
});

describe('Capture image srcset =>', () => {
it('make request call to capture resource', async () => {
let responsiveDOM = dedent`
Expand Down
32 changes: 30 additions & 2 deletions packages/core/test/utils.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { decodeAndEncodeURLWithLogging } from '../src/utils.js';
import { logger } from './helpers/index.js';
import { decodeAndEncodeURLWithLogging, waitForSelectorInsideBrowser } from '../src/utils.js';
import { logger, setupTest } from './helpers/index.js';
import percyLogger from '@percy/logger';
import Percy from '@percy/core';

describe('utils', () => {
let log;
Expand Down Expand Up @@ -45,4 +46,31 @@ describe('utils', () => {
expect(logger.stderr).toEqual(['[percy] some Warning Message']);
});
});
describe('waitForSelectorInsideBrowser', () => {
it('waitForSelectorInsideBrowser should work when called with correct parameters', async () => {
await setupTest();
let percy = await Percy.start({
token: 'PERCY_TOKEN',
snapshot: { widths: [1000] },
discovery: { concurrency: 1 }
});
const page = await percy.browser.page();
spyOn(page, 'eval').and.callThrough();
waitForSelectorInsideBrowser(page, 'body', 30000);

expect(page.eval).toHaveBeenCalledWith('await waitForSelector("body", 30000)');
});
it('should handle errors when waitForSelectorInsideBrowser fails', async () => {
let error = null;
const expectedError = new Error('Unable to find: body');

try {
await waitForSelectorInsideBrowser(null, 'body', 30000);
} catch (e) {
error = e;
}

expect(error).toEqual(expectedError);
});
});
});
Loading