Skip to content

Commit c9c64dd

Browse files
committed
reland: testrunner: make environment a simple class
This re-lands PR microsoft#2769 It was reverted before in microsoft#2790 because it was breaking the new CHANNEL bot.
1 parent 43cdb3b commit c9c64dd

File tree

8 files changed

+375
-388
lines changed

8 files changed

+375
-388
lines changed

test/environments.js

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
/**
2+
* Copyright 2019 Google Inc. All rights reserved.
3+
* Modifications copyright (c) Microsoft Corporation.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
const utils = require('./utils');
19+
const fs = require('fs');
20+
const path = require('path');
21+
const rm = require('rimraf').sync;
22+
const {TestServer} = require('../utils/testserver/');
23+
const { DispatcherConnection } = require('../lib/rpc/server/dispatcher');
24+
const { Connection } = require('../lib/rpc/client/connection');
25+
const { BrowserTypeDispatcher } = require('../lib/rpc/server/browserTypeDispatcher');
26+
27+
class ServerEnvironment {
28+
async beforeAll(state) {
29+
const assetsPath = path.join(__dirname, 'assets');
30+
const cachedPath = path.join(__dirname, 'assets', 'cached');
31+
32+
const port = 8907 + state.parallelIndex * 2;
33+
state.server = await TestServer.create(assetsPath, port);
34+
state.server.enableHTTPCache(cachedPath);
35+
state.server.PORT = port;
36+
state.server.PREFIX = `http://localhost:${port}`;
37+
state.server.CROSS_PROCESS_PREFIX = `http://127.0.0.1:${port}`;
38+
state.server.EMPTY_PAGE = `http://localhost:${port}/empty.html`;
39+
40+
const httpsPort = port + 1;
41+
state.httpsServer = await TestServer.createHTTPS(assetsPath, httpsPort);
42+
state.httpsServer.enableHTTPCache(cachedPath);
43+
state.httpsServer.PORT = httpsPort;
44+
state.httpsServer.PREFIX = `https://localhost:${httpsPort}`;
45+
state.httpsServer.CROSS_PROCESS_PREFIX = `https://127.0.0.1:${httpsPort}`;
46+
state.httpsServer.EMPTY_PAGE = `https://localhost:${httpsPort}/empty.html`;
47+
}
48+
49+
async afterAll({server, httpsServer}) {
50+
await Promise.all([
51+
server.stop(),
52+
httpsServer.stop(),
53+
]);
54+
}
55+
56+
async beforeEach(state) {
57+
state.server.reset();
58+
state.httpsServer.reset();
59+
}
60+
}
61+
62+
class DefaultBrowserOptionsEnvironment {
63+
constructor(defaultBrowserOptions, dumpLogOnFailure, playwrightPath) {
64+
this._defaultBrowserOptions = defaultBrowserOptions;
65+
this._dumpLogOnFailure = dumpLogOnFailure;
66+
this._playwrightPath = playwrightPath;
67+
this._loggerSymbol = Symbol('DefaultBrowserOptionsEnvironment.logger');
68+
}
69+
70+
async beforeAll(state) {
71+
state[this._loggerSymbol] = utils.createTestLogger(this._dumpLogOnFailure, null, 'extra');
72+
state.defaultBrowserOptions = {
73+
...this._defaultBrowserOptions,
74+
logger: state[this._loggerSymbol],
75+
};
76+
state.playwrightPath = this._playwrightPath;
77+
}
78+
79+
async beforeEach(state, testRun) {
80+
state[this._loggerSymbol].setTestRun(testRun);
81+
}
82+
83+
async afterEach(state) {
84+
state[this._loggerSymbol].setTestRun(null);
85+
}
86+
}
87+
88+
// simulate globalSetup per browserType that happens only once regardless of TestWorker.
89+
const hasBeenCleaned = new Set();
90+
91+
class GoldenEnvironment {
92+
async beforeAll(state) {
93+
const { OUTPUT_DIR, GOLDEN_DIR } = utils.testOptions(state.browserType);
94+
if (!hasBeenCleaned.has(state.browserType)) {
95+
hasBeenCleaned.add(state.browserType);
96+
if (fs.existsSync(OUTPUT_DIR))
97+
rm(OUTPUT_DIR);
98+
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
99+
}
100+
state.golden = goldenName => ({ goldenPath: GOLDEN_DIR, outputPath: OUTPUT_DIR, goldenName });
101+
}
102+
103+
async afterAll(state) {
104+
delete state.golden;
105+
}
106+
107+
async afterEach(state, testRun) {
108+
if (state.browser && state.browser.contexts().length !== 0) {
109+
if (testRun.ok())
110+
console.warn(`\nWARNING: test "${testRun.test().fullName()}" (${testRun.test().location()}) did not close all created contexts!\n`);
111+
await Promise.all(state.browser.contexts().map(context => context.close()));
112+
}
113+
}
114+
}
115+
116+
class TraceTestEnvironment {
117+
static enableForTest(test) {
118+
test.setTimeout(100000000);
119+
test.addEnvironment(new TraceTestEnvironment());
120+
}
121+
122+
constructor() {
123+
this._session = null;
124+
}
125+
126+
async beforeEach() {
127+
const inspector = require('inspector');
128+
const fs = require('fs');
129+
const util = require('util');
130+
const url = require('url');
131+
const readFileAsync = util.promisify(fs.readFile.bind(fs));
132+
this._session = new inspector.Session();
133+
this._session.connect();
134+
const postAsync = util.promisify(this._session.post.bind(this._session));
135+
await postAsync('Debugger.enable');
136+
const setBreakpointCommands = [];
137+
const N = t.body().toString().split('\n').length;
138+
const location = t.location();
139+
const lines = (await readFileAsync(location.filePath(), 'utf8')).split('\n');
140+
for (let line = 0; line < N; ++line) {
141+
const lineNumber = line + location.lineNumber();
142+
setBreakpointCommands.push(postAsync('Debugger.setBreakpointByUrl', {
143+
url: url.pathToFileURL(location.filePath()),
144+
lineNumber,
145+
condition: `console.log('${String(lineNumber + 1).padStart(6, ' ')} | ' + ${JSON.stringify(lines[lineNumber])})`,
146+
}).catch(e => {}));
147+
}
148+
await Promise.all(setBreakpointCommands);
149+
}
150+
151+
async afterEach() {
152+
this._session.disconnect();
153+
}
154+
}
155+
156+
class PlaywrightEnvironment {
157+
constructor(playwright) {
158+
this._playwright = playwright;
159+
}
160+
161+
name() { return 'Playwright'; };
162+
beforeAll(state) { state.playwright = this._playwright; }
163+
afterAll(state) { delete state.playwright; }
164+
}
165+
166+
class BrowserTypeEnvironment {
167+
constructor(browserType) {
168+
this._browserType = browserType;
169+
}
170+
171+
async beforeAll(state) {
172+
// Channel substitute
173+
let overridenBrowserType = this._browserType;
174+
if (process.env.PWCHANNEL) {
175+
const dispatcherConnection = new DispatcherConnection();
176+
const connection = new Connection();
177+
dispatcherConnection.onmessage = async message => {
178+
setImmediate(() => connection.dispatch(message));
179+
};
180+
connection.onmessage = async message => {
181+
const result = await dispatcherConnection.dispatch(message);
182+
await new Promise(f => setImmediate(f));
183+
return result;
184+
};
185+
new BrowserTypeDispatcher(dispatcherConnection.rootScope(), this._browserType);
186+
overridenBrowserType = await connection.waitForObjectWithKnownName(this._browserType.name());
187+
}
188+
state.browserType = overridenBrowserType;
189+
}
190+
191+
async afterAll(state) {
192+
delete state.browserType;
193+
}
194+
}
195+
196+
class BrowserEnvironment {
197+
constructor(browserType, launchOptions, dumpLogOnFailure) {
198+
this._browserType = browserType;
199+
this._launchOptions = launchOptions;
200+
this._dumpLogOnFailure = dumpLogOnFailure;
201+
this._loggerSymbol = Symbol('BrowserEnvironment.logger');
202+
}
203+
204+
name() { return this._browserType.name(); }
205+
206+
async beforeAll(state) {
207+
state[this._loggerSymbol] = utils.createTestLogger(this._dumpLogOnFailure);
208+
state.browser = await this._browserType.launch({
209+
...this._launchOptions,
210+
logger: state[this._loggerSymbol],
211+
});
212+
}
213+
214+
async afterAll(state) {
215+
await state.browser.close();
216+
delete state.browser;
217+
}
218+
219+
async beforeEach(state, testRun) {
220+
state[this._loggerSymbol].setTestRun(testRun);
221+
}
222+
223+
async afterEach(state, testRun) {
224+
state[this._loggerSymbol].setTestRun(null);
225+
}
226+
}
227+
228+
class PageEnvironment {
229+
async beforeEach(state) {
230+
state.context = await state.browser.newContext();
231+
state.page = await state.context.newPage();
232+
}
233+
234+
async afterEach(state) {
235+
await state.context.close();
236+
state.context = null;
237+
state.page = null;
238+
}
239+
}
240+
241+
module.exports = {
242+
ServerEnvironment,
243+
GoldenEnvironment,
244+
TraceTestEnvironment,
245+
DefaultBrowserOptionsEnvironment,
246+
PlaywrightEnvironment,
247+
BrowserTypeEnvironment,
248+
BrowserEnvironment,
249+
PageEnvironment,
250+
};

test/page.spec.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,3 +1305,9 @@ describe('Page api coverage', function() {
13051305
expect(await frame.evaluate(() => document.querySelector('textarea').value)).toBe('a');
13061306
});
13071307
});
1308+
1309+
describe.skip(!CHANNEL)('Page channel', function() {
1310+
it('page should be client stub', async({page, server}) => {
1311+
expect(!!page._channel).toBeTruthy();
1312+
});
1313+
});

0 commit comments

Comments
 (0)