ChromeDevTools / devtools-protocol

Chrome DevTools Protocol
https://chromedevtools.github.io/devtools-protocol/
BSD 3-Clause "New" or "Revised" License
1.15k stars 226 forks source link

Network.emulateNetworkConditions not put into effect unless devtools opened #186

Closed ajohnson052 closed 4 years ago

ajohnson052 commented 5 years ago

The problem as we understand it:

Network.emulateNetworkConditions commands don't appear to be put into effect by the browser unless:

  1. the devtools UI is already open when the protocol message is sent

  2. the devtools UI is opened sometime after Network.emulateNetworkConditions is sent (in which case it's put into effect at that time)

What we want:

To write a headless test that toggles both the browser and our service worker offline in order to test offline functionality of our service worker

What we tried:

const puppeteer = require("puppeteer");

(async () => {
  const browser = await puppeteer.launch({ devtools: true });
  const [ page ] = await browser.pages();

  await page.goto('http://localhost:3000');

  // This, we think, gives time for the service worker to register/install
  await new Promise((resolve) => setTimeout(resolve, 1000));
  await page.reload();

  const pageTarget = await browser.waitForTarget(target => target.type() === "page")
  const pageSession = await pageTarget.createCDPSession();
  await pageSession.send("Network.emulateNetworkConditions", {
    offline: true,
    latency: 2000,
    downloadThroughput: 2000,
    uploadThroughput: 2000
  });

  const serviceWorkerTarget = await browser.waitForTarget(target => target.type() === "service_worker");
  const serviceWorkerSession = await serviceWorkerTarget.createCDPSession();
  await serviceWorkerSession.send("Network.emulateNetworkConditions", {
    offline: true,
    latency: 2000,
    downloadThroughput: 2000,
    uploadThroughput: 2000
  });

  await page.type('input', 'foo');

  // Submits HTTP POST which is caught by our service worker and enqueued for BackgroundSync
  await page.keyboard.press('Enter');
  debugger;
})();

With this script we have seen a POST request enqueued in indexedDB (which is what we would expect if the user was indeed offline).

However, if we use puppeteer.launch() with { devtools: false }, or with { headless: true }, our POST request is not enqueued.

Perhaps we're neglecting to enable some feature of our page or service worker. Is this expected behavior?

paulirish commented 5 years ago

try a "Network.enable" previous to the emulate call?

It's a bit atypical, but we know that Network.setUserAgent doesn't succeed without Network.enable first.

ajohnson052 commented 5 years ago

Thanks for the suggestion! Unfortunately, I don't see any change in behavior after sending Network.enable to both the pageSession and the serviceWorkerSession.

Any other ideas we should try?

TomDevs commented 5 years ago

I'm also expereincing this issue. Tried with and without calling Network.enable before calling Network.emulateNetworkConditions. It successfully works if devtools is open but not if it's closed.

gholden3 commented 5 years ago

bitmoji .

tysoeh commented 5 years ago

I'm encountering something similar if not the same. Emulating browser conditions for service worker targets is crucial for our app; there don't seem to be any other decent ways to write e2e tests that get at offline functionality of service workers.

From looking into this problem, I've learned that service workers don't have a javascript runtime of their own. Instead, they use a dedicated web worker for executing javascript, and if you want to execute javascript in the service worker via CDP, you must send a Runtime.enable command first. Maybe the devtools UI does the equivalent of sending Runtime.enable to the service worker, and that's what we're missing in our approach.

TomDevs commented 5 years ago

@tysoeh Tried what you've suggested by sending Runtime.enable but that didn't seem to fix the issue.

I'm using Cypress with Electron 76 (also tested using Chrome 72 and Chromium 76) for my e2e tests. I'll try and explain the process I'm using for this.

Again, this only seems to work if devtools has been opened before Network.emulateNetworkConditions is sent.

If devtools has not been opened, window.navigator.onLine evaluates to false after sending Network.emulateNetworkConditions, but the page can still fetch data.

If devtools is open, window.navigator.onLine also evaluates to false but the page is blocked from fetching data.

Hopefully this information can help someone with more knowledge of the DevTools Protocol narrow down the issue.

TimvdLippe commented 4 years ago

This repository is related to Chrome DevTools Protocol, but does not track issues regarding its definition or implementation. If you want to file an issue for the Chrome DevTools Protocol, please open an issue on https://crbug.com under component: Platform>DevTools>Platform. Thanks in advance!