diff --git a/core/gather/driver/wait-for-condition.js b/core/gather/driver/wait-for-condition.js index 08340b946cbd..ac047322f59e 100644 --- a/core/gather/driver/wait-for-condition.js +++ b/core/gather/driver/wait-for-condition.js @@ -487,6 +487,18 @@ async function waitForFullyLoaded(session, networkMonitor, options) { throw new LighthouseError(LighthouseError.errors.PAGE_HUNG); } + // Log remaining inflight requests if any. + const inflightRequestUrls = networkMonitor + .getInflightRequests() + .map((request) => request.url); + if (inflightRequestUrls.length > 0) { + log.warn( + 'waitFor', + 'Remaining inflight requests URLs', + inflightRequestUrls + ); + } + return {timedOut: true}; }; }); diff --git a/core/test/gather/driver/wait-for-condition-test.js b/core/test/gather/driver/wait-for-condition-test.js index ee96db18d807..5e190d74998d 100644 --- a/core/test/gather/driver/wait-for-condition-test.js +++ b/core/test/gather/driver/wait-for-condition-test.js @@ -4,6 +4,8 @@ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +import log from 'lighthouse-logger'; + import * as wait from '../../../gather/driver/wait-for-condition.js'; import { mockCommands, @@ -63,7 +65,7 @@ describe('waitForFullyLoaded()', () => { beforeEach(() => { session = {sendCommand: fnAny().mockResolvedValue(), setNextProtocolTimeout: fnAny()}; - networkMonitor = {}; + networkMonitor = {getInflightRequests: fnAny().mockReturnValue([])}; const overrides = { waitForFcp: createMockWaitForFn(), @@ -179,10 +181,17 @@ describe('waitForFullyLoaded()', () => { expect(await loadPromise).toMatchObject({timedOut: false}); }); - it('should timeout when not resolved fast enough', async () => { + it('should timeout and warn when not resolved fast enough', async () => { options._waitForTestOverrides.waitForLoadEvent = createMockWaitForFn(); options._waitForTestOverrides.waitForNetworkIdle = createMockWaitForFn(); options._waitForTestOverrides.waitForCPUIdle = createMockWaitForFn(); + networkMonitor.getInflightRequests.mockReturnValue([{url: 'https://example.com'}]); + + /** @type {Array} */ + const warnings = []; + /** @param {unknown} evt */ + const saveWarning = evt => warnings.push(evt); + log.events.on('warning', saveWarning); const loadPromise = makePromiseInspectable(wait.waitForFullyLoaded( session, @@ -203,6 +212,22 @@ describe('waitForFullyLoaded()', () => { expect(options._waitForTestOverrides.waitForLoadEvent.getMockCancelFn()).toHaveBeenCalled(); expect(options._waitForTestOverrides.waitForNetworkIdle.getMockCancelFn()).toHaveBeenCalled(); expect(options._waitForTestOverrides.waitForCPUIdle.getMockCancelFn()).toHaveBeenCalled(); + // Check for warn logs + log.events.off('warning', saveWarning); + expect(warnings).toEqual([ + [ + 'waitFor', + 'Timed out waiting for page load. Checking if page is hung...', + ], + [ + 'waitFor', + 'Remaining inflight requests URLs', + [ + 'https://example.com', + ], + ], + ]); + expect(await loadPromise).toMatchObject({timedOut: true}); });