Skip to content

Commit 2b4e88b

Browse files
authored
Use iframes instead of tabs (GoogleChrome#2928)
1 parent 16fa3de commit 2b4e88b

4 files changed

Lines changed: 97 additions & 118 deletions

File tree

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
Copyright 2021 Google LLC
3+
4+
Use of this source code is governed by an MIT-style
5+
license that can be found in the LICENSE file or at
6+
https://opensource.org/licenses/MIT.
7+
*/
8+
9+
const PREFIX = 'iframe-';
10+
const waitUntil = require('../wait-until');
11+
12+
class Client {
13+
constructor(driver, id) {
14+
this._driver = driver;
15+
this._id = id;
16+
}
17+
18+
async executeAsyncScript(code) {
19+
const value = await this._driver.executeAsyncScript((id, code, cb) => {
20+
const iframe = document.querySelector(`#${id}`);
21+
Promise.resolve(iframe.contentWindow.eval(code))
22+
.then((value) => cb(value))
23+
.catch((err) => cb(err.toString()));
24+
}, this._id, code);
25+
26+
return value;
27+
}
28+
29+
async wait(code) {
30+
await waitUntil(() => this.executeAsyncScript(code));
31+
}
32+
33+
remove() {
34+
this._driver.executeScript((id) => {
35+
const el = document.querySelector(`#${id}`);
36+
document.body.removeChild(el);
37+
}, this._id);
38+
}
39+
}
40+
41+
/**
42+
* Wraps methods in the underlying webdriver API to create, switch between,
43+
* and close tabs in a browser.
44+
*/
45+
class IframeManager {
46+
/**
47+
* @param {WebDriver} driver
48+
*
49+
* @private
50+
*/
51+
constructor(driver) {
52+
this._driver = driver;
53+
this._clients = new Set();
54+
}
55+
56+
async createIframeClient(url) {
57+
const iframeId = await this._driver.executeAsyncScript((url, prefix, cb) => {
58+
const el = document.createElement('iframe');
59+
if (!('iframeCount' in window)) {
60+
window.iframeCount = 1;
61+
}
62+
const id = `${prefix}${window.iframeCount++}`;
63+
el.addEventListener('load', () => {
64+
cb(id);
65+
});
66+
el.src = url;
67+
el.id = id;
68+
document.body.appendChild(el);
69+
}, url, PREFIX);
70+
71+
return new Client(this._driver, iframeId);
72+
}
73+
}
74+
75+
module.exports = {IframeManager};

infra/testing/webdriver/TabManager.js

Lines changed: 0 additions & 49 deletions
This file was deleted.

test/workbox-broadcast-update/integration/test-all.js

Lines changed: 10 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@
99
const expect = require('chai').expect;
1010

1111
const {runUnitTests} = require('../../../infra/testing/webdriver/runUnitTests');
12-
const {TabManager} = require('../../../infra/testing/webdriver/TabManager');
12+
const {IframeManager} = require('../../../infra/testing/webdriver/IframeManager');
1313
const activateAndControlSW = require('../../../infra/testing/activate-and-control');
1414
const cleanSWEnv = require('../../../infra/testing/clean-sw');
1515
const templateData = require('../../../infra/testing/server/template-data');
1616

1717
// Store local references of these globals.
18-
const {webdriver, server, seleniumBrowser} = global.__workbox;
18+
const {webdriver, server} = global.__workbox;
1919

2020
describe(`[workbox-broadcast-update]`, function() {
2121
it(`passes all SW unit tests`, async function() {
@@ -122,13 +122,7 @@ describe(`[workbox-broadcast-update] Plugin`, function() {
122122
});
123123

124124
it(`should broadcast a message to all open window clients by default`, async function() {
125-
// This test doesn't work in Safari:
126-
// https://github.com/GoogleChrome/workbox/issues/2755
127-
if (seleniumBrowser.getId() === 'safari') {
128-
this.skip();
129-
}
130-
131-
const tabManager = new TabManager(webdriver);
125+
const iframeManager = new IframeManager(webdriver);
132126

133127
templateData.assign({
134128
title: 'Broadcast Cache Update Test',
@@ -152,15 +146,11 @@ describe(`[workbox-broadcast-update] Plugin`, function() {
152146
}, dynamicPageURL);
153147
});
154148

155-
// Update the template data, then open a new tab to trigger the update.
149+
// Update the template data and open an iframe to trigger the cache update.
156150
templateData.assign({
157151
body: 'Third test, with an updated body.',
158152
});
159-
160-
await tabManager.openTab(dynamicPageURL);
161-
162-
// Go back to the initial tab to assert the message was received there.
163-
await tabManager.closeOpenedTabs();
153+
await iframeManager.createIframeClient(dynamicPageURL);
164154

165155
await webdriver.wait(() => {
166156
return webdriver.executeScript(() => {
@@ -183,34 +173,19 @@ describe(`[workbox-broadcast-update] Plugin`, function() {
183173
});
184174

185175
it(`should only broadcast a message to the client that made the request when notifyAllClients is false`, async function() {
186-
// This test doesn't work in Safari:
187-
// https://github.com/GoogleChrome/workbox/issues/2755
188-
if (seleniumBrowser.getId() === 'safari') {
189-
this.skip();
190-
}
191-
192176
const url = `${apiURL}?notifyAllClientsTest`;
193-
const tabManager = new TabManager(webdriver);
177+
const iframeManager = new IframeManager(webdriver);
194178

195179
await webdriver.get(testingURL);
196180
await webdriver.executeAsyncScript((url, cb) => {
197181
fetch(url).then(() => cb()).catch((err) => cb(err.message));
198182
}, url);
199183

200-
await tabManager.openTab(testingURL);
201-
await webdriver.executeAsyncScript((url, cb) => {
202-
fetch(url).then(() => cb()).catch((err) => cb(err.message));
203-
}, url);
204-
205-
await webdriver.wait(() => {
206-
return webdriver.executeScript(() => {
207-
return window.__messages.length > 0;
208-
});
209-
});
184+
const iframeClient = await iframeManager.createIframeClient(testingURL);
185+
await iframeClient.executeAsyncScript(`fetch(${JSON.stringify(url)})`);
186+
await iframeClient.wait('window.__messages.length > 0');
210187

211-
const populatedMessages = await webdriver.executeScript(() => {
212-
return window.__messages;
213-
});
188+
const populatedMessages = await iframeClient.executeAsyncScript('window.__messages');
214189
expect(populatedMessages).to.eql([{
215190
type: 'CACHE_UPDATED',
216191
meta: 'workbox-broadcast-update',
@@ -220,8 +195,6 @@ describe(`[workbox-broadcast-update] Plugin`, function() {
220195
},
221196
}]);
222197

223-
await tabManager.closeOpenedTabs();
224-
225198
const unpopulatedMessages = await webdriver.executeScript(() => {
226199
return window.__messages;
227200
});

test/workbox-window/integration/test-all.js

Lines changed: 12 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ const {expect} = require('chai');
1010

1111
const {executeAsyncAndCatch} = require('../../../infra/testing/webdriver/executeAsyncAndCatch');
1212
const {runUnitTests} = require('../../../infra/testing/webdriver/runUnitTests');
13-
const {TabManager} = require('../../../infra/testing/webdriver/TabManager');
13+
const {IframeManager} = require('../../../infra/testing/webdriver/IframeManager');
1414
const {unregisterAllSWs} = require('../../../infra/testing/webdriver/unregisterAllSWs');
1515
const {windowLoaded} = require('../../../infra/testing/webdriver/windowLoaded');
1616
const templateData = require('../../../infra/testing/server/template-data');
1717

1818
// Store local references of these globals.
19-
const {webdriver, server, seleniumBrowser} = global.__workbox;
19+
const {webdriver, server} = global.__workbox;
2020

2121
const testServerOrigin = server.getAddress();
2222
const testPath = `${testServerOrigin}/test/workbox-window/static/`;
@@ -160,14 +160,7 @@ describe(`[workbox-window] Workbox`, function() {
160160
});
161161

162162
it(`reports all events for an external SW registration`, async function() {
163-
// This test doesn't work in Safari or Firefox:
164-
// https://github.com/GoogleChrome/workbox/issues/2755
165-
if (seleniumBrowser.getId() === 'safari' ||
166-
seleniumBrowser.getId() === 'firefox') {
167-
this.skip();
168-
}
169-
170-
const tabManager = new TabManager(webdriver);
163+
const iframeManager = new IframeManager(webdriver);
171164

172165
await executeAsyncAndCatch(async (cb) => {
173166
try {
@@ -200,30 +193,17 @@ describe(`[workbox-window] Workbox`, function() {
200193
// Update the version in sw.js to trigger a new installation.
201194
templateData.assign({version: '2'});
202195

203-
const secondTabPath = `${testPath}?second`;
204-
await tabManager.openTab(secondTabPath);
205-
await windowLoaded();
206-
207-
const location = await executeAsyncAndCatch(async (cb) => {
208-
try {
209-
const wb = new Workbox('sw-clients-claim.js.njk');
210-
211-
await wb.register();
212-
213-
// Resolve this execution block once the updated SW is in control.
214-
await window.activatedAndControlling(wb);
215-
cb(location.href);
216-
} catch (error) {
217-
cb({error: error.stack});
218-
}
219-
});
196+
const secondPath = `${testPath}?second`;
197+
const iframeClient = await iframeManager.createIframeClient(secondPath);
198+
const location = await iframeClient.executeAsyncScript(`
199+
const wb = new Workbox('sw-clients-claim.js.njk');
200+
wb.register()
201+
.then(() => window.activatedAndControlling(wb))
202+
.then(() => location.href);
203+
`);
220204

221205
// Just confirm we're operating on the page we expect.
222-
expect(location).to.eql(secondTabPath);
223-
224-
// Close the second tab and switch back to the first tab before
225-
// executing the following block.
226-
await tabManager.closeOpenedTabs();
206+
expect(location).to.eql(secondPath);
227207

228208
const result = await executeAsyncAndCatch(async (cb) => {
229209
cb({

0 commit comments

Comments
 (0)