Skip to content

Commit

Permalink
Listen to requests until selector is present (#243)
Browse files Browse the repository at this point in the history
  • Loading branch information
oblador authored Jun 11, 2020
1 parent 8f5f5fe commit de5a05d
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 53 deletions.
21 changes: 21 additions & 0 deletions packages/browser/src/await-selector-present.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const awaitSelectorPresent = (window, selector, timeout = 10000) =>
new Promise((resolve, reject) => {
let resolutionTimer;
const rejectionTimer = setTimeout(() => {
clearTimeout(resolutionTimer);
reject(new Error(`Timeout after ${timeout}ms`));
}, timeout);

const waitForSelector = () => {
if (window.document.querySelector(selector)) {
clearTimeout(rejectionTimer);
resolve();
} else {
resolutionTimer = setTimeout(waitForSelector, 100);
}
};

waitForSelector();
});

module.exports = awaitSelectorPresent;
2 changes: 2 additions & 0 deletions packages/browser/src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const addLokiSessionMarker = require('./add-loki-session-marker');
const awaitLokiReady = require('./await-loki-ready');
const awaitSelectorPresent = require('./await-selector-present');
const createStorybookConfigurator = require('./configure-storybook');
const disableAnimations = require('./disable-animations');
const disableInputCaret = require('./disable-input-caret');
Expand All @@ -10,6 +11,7 @@ const getStories = require('./get-stories');
module.exports = {
addLokiSessionMarker,
awaitLokiReady,
awaitSelectorPresent,
createStorybookConfigurator,
disableAnimations,
disableInputCaret,
Expand Down
125 changes: 72 additions & 53 deletions packages/target-chrome-core/src/create-chrome-target.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const {
getSelectorBoxSize,
getStories,
awaitLokiReady,
awaitSelectorPresent,
addLokiSessionMarker,
} = require('@loki/browser');
const {
Expand Down Expand Up @@ -71,64 +72,70 @@ function createChromeTarget(
await Emulation.setEmulatedMedia({ media: options.media });
}

const awaitRequestsFinished = () =>
new Promise(async (resolve, reject) => {
const pendingRequestURLMap = {};
const failedURLs = [];
let pageLoaded = false;
let stabilizationTimer = null;

const maybeFulfillPromise = () => {
if (pageLoaded && Object.keys(pendingRequestURLMap).length === 0) {
if (failedURLs.length !== 0) {
reject(new FetchingURLsError(failedURLs));
} else {
// In some cases such as fonts further requests will only happen after the page has been fully rendered
if (stabilizationTimer) {
clearTimeout(stabilizationTimer);
}
stabilizationTimer = setTimeout(
resolve,
REQUEST_STABILIZATION_TIMEOUT
);
}
}
};
const pendingRequestURLMap = {};
const failedURLs = [];
let stabilizationTimer = null;
let requestsFinishedAwaiter;

const requestEnded = requestId => {
delete pendingRequestURLMap[requestId];
maybeFulfillPromise();
};

const requestFailed = requestId => {
const failedURL = pendingRequestURLMap[requestId];
if (!fetchFailIgnore || !fetchFailIgnore.test(failedURL)) {
failedURLs.push(failedURL);
}
requestEnded(requestId);
};
const maybeFulfillPromise = () => {
if (!requestsFinishedAwaiter) {
return;
}
const { reject, resolve } = requestsFinishedAwaiter;

Network.requestWillBeSent(({ requestId, request }) => {
if (Object.keys(pendingRequestURLMap).length === 0) {
if (failedURLs.length !== 0) {
reject(new FetchingURLsError(failedURLs));
} else {
// In some cases such as fonts further requests will only happen after the page has been fully rendered
if (stabilizationTimer) {
clearTimeout(stabilizationTimer);
}
pendingRequestURLMap[requestId] = request.url;
});

Network.responseReceived(({ requestId, response }) => {
if (response.status >= 400) {
requestFailed(requestId);
} else {
requestEnded(requestId);
}
});
stabilizationTimer = setTimeout(
resolve,
REQUEST_STABILIZATION_TIMEOUT
);
}
}
};

const startObservingRequests = () => {
const requestEnded = requestId => {
delete pendingRequestURLMap[requestId];
maybeFulfillPromise();
};

const requestFailed = requestId => {
const failedURL = pendingRequestURLMap[requestId];
if (!fetchFailIgnore || !fetchFailIgnore.test(failedURL)) {
failedURLs.push(failedURL);
}
requestEnded(requestId);
};

Network.requestWillBeSent(({ requestId, request }) => {
if (stabilizationTimer) {
clearTimeout(stabilizationTimer);
}
pendingRequestURLMap[requestId] = request.url;
});

Network.loadingFailed(({ requestId }) => {
Network.responseReceived(({ requestId, response }) => {
if (response.status >= 400) {
requestFailed(requestId);
});
} else {
requestEnded(requestId);
}
});

await Page.loadEventFired();
pageLoaded = true;
Network.loadingFailed(({ requestId }) => {
requestFailed(requestId);
});
};

const awaitRequestsFinished = () =>
new Promise((resolve, reject) => {
requestsFinishedAwaiter = { resolve, reject };
maybeFulfillPromise();
});

Expand Down Expand Up @@ -159,7 +166,7 @@ function createChromeTarget(

client.executeFunctionWithWindow = executeFunctionWithWindow;

client.loadUrl = async url => {
client.loadUrl = async (url, selectorToBePresent) => {
if (!options.chromeEnableAnimations) {
debug('Disabling animations');
await evaluateOnNewDocument(`(${disableAnimations})(window);`);
Expand All @@ -168,7 +175,19 @@ function createChromeTarget(
await evaluateOnNewDocument(`(${disableInputCaret})(window);`);

debug(`Navigating to ${url}`);
await Promise.all([Page.navigate({ url }), awaitRequestsFinished()]);
startObservingRequests();
await Page.navigate({ url });
await Page.loadEventFired();

if (selectorToBePresent) {
debug(`Awaiting selector "${selectorToBePresent}"`);
await executeFunctionWithWindow(
awaitSelectorPresent,
selectorToBePresent
);
}

await awaitRequestsFinished();

debug('Awaiting runtime setup');
await executeFunctionWithWindow(awaitLokiReady);
Expand Down Expand Up @@ -323,7 +342,7 @@ function createChromeTarget(
const tab = await launchNewTab(tabOptions);
let screenshot;
try {
await withTimeout(options.chromeLoadTimeout)(tab.loadUrl(url));
await withTimeout(options.chromeLoadTimeout)(tab.loadUrl(url, selector));
screenshot = await tab.captureScreenshot(selector);
} catch (err) {
if (err instanceof TimeoutError) {
Expand Down

0 comments on commit de5a05d

Please sign in to comment.