From 51ecf8c605648e37b6542b4e111fdb5da8160f26 Mon Sep 17 00:00:00 2001 From: amandeepsingh333 Date: Tue, 10 Sep 2024 16:25:50 +0530 Subject: [PATCH] added config for waitForTimeout and waitForSelector --- packages/core/src/config.js | 10 ++++ packages/core/src/discovery.js | 17 ++++++- packages/core/src/snapshot.js | 2 + packages/core/src/utils.js | 9 ++++ packages/core/test/discovery.test.js | 71 ++++++++++++++++++++++++++++ packages/core/test/utils.test.js | 32 ++++++++++++- 6 files changed, 138 insertions(+), 3 deletions(-) diff --git a/packages/core/src/config.js b/packages/core/src/config.js index 48d50b11b..017e042cd 100644 --- a/packages/core/src/config.js +++ b/packages/core/src/config.js @@ -196,6 +196,14 @@ export const configSchema = { maximum: 750, minimum: 1 }, + waitForSelector: { + type: 'string' + }, + waitForTimeout: { + type: 'integer', + minimum: 1, + maximum: 30000 + }, disableCache: { type: 'boolean' }, @@ -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' }, diff --git a/packages/core/src/discovery.js b/packages/core/src/discovery.js index dc57ffad0..743d90e44 100644 --- a/packages/core/src/discovery.js +++ b/packages/core/src/discovery.js @@ -1,5 +1,6 @@ import logger from '@percy/logger'; import Queue from './queue.js'; +import Page from './page.js'; import { normalizeURL, hostnameMatches, @@ -9,7 +10,9 @@ import { createLogResource, yieldAll, snapshotLogName, - withRetries + waitForTimeout, + withRetries, + waitForSelectorInsideBrowser } from './utils.js'; import { sha256hash @@ -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 */ diff --git a/packages/core/src/snapshot.js b/packages/core/src/snapshot.js index 341a42b33..6d315a2ef 100644 --- a/packages/core/src/snapshot.js +++ b/packages/core/src/snapshot.js @@ -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, diff --git a/packages/core/src/utils.js b/packages/core/src/utils.js index 81fc06e38..163fe7702 100644 --- a/packages/core/src/utils.js +++ b/packages/core/src/utils.js @@ -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) { diff --git a/packages/core/test/discovery.test.js b/packages/core/test/discovery.test.js index 318a590f8..d4e2ca9d8 100644 --- a/packages/core/test/discovery.test.js +++ b/packages/core/test/discovery.test.js @@ -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` diff --git a/packages/core/test/utils.test.js b/packages/core/test/utils.test.js index 2a43d7c8f..8a2f15790 100644 --- a/packages/core/test/utils.test.js +++ b/packages/core/test/utils.test.js @@ -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; @@ -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); + }); + }); });