@playwright/test 1.31.2 → 1.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/cli.js +12 -34
- package/lib/common/compilationCache.js +4 -2
- package/lib/common/configLoader.js +4 -1
- package/lib/common/fixtures.js +31 -2
- package/lib/common/globals.js +9 -0
- package/lib/common/process.js +15 -3
- package/lib/common/testType.js +2 -1
- package/lib/common/transform.js +0 -1
- package/lib/index.js +95 -56
- package/lib/isomorphic/folders.js +25 -0
- package/lib/isomorphic/teleReceiver.js +368 -0
- package/lib/matchers/expect.js +35 -16
- package/lib/matchers/matchers.js +48 -48
- package/lib/matchers/toBeTruthy.js +1 -1
- package/lib/matchers/toEqual.js +1 -4
- package/lib/matchers/toMatchSnapshot.js +3 -4
- package/lib/matchers/toMatchText.js +1 -1
- package/lib/plugins/vitePlugin.js +3 -2
- package/lib/reporters/base.js +6 -9
- package/lib/reporters/html.js +7 -2
- package/lib/reporters/json.js +2 -2
- package/lib/reporters/junit.js +2 -2
- package/lib/reporters/teleEmitter.js +209 -0
- package/lib/runner/dispatcher.js +15 -2
- package/lib/runner/loadUtils.js +168 -96
- package/lib/runner/loaderHost.js +2 -2
- package/lib/runner/processHost.js +16 -4
- package/lib/runner/reporters.js +3 -2
- package/lib/runner/runner.js +9 -1
- package/lib/runner/taskRunner.js +6 -8
- package/lib/runner/tasks.js +55 -46
- package/lib/runner/testGroups.js +15 -10
- package/lib/runner/uiMode.js +279 -0
- package/lib/runner/workerHost.js +7 -6
- package/lib/store.js +99 -0
- package/lib/util.js +23 -39
- package/lib/worker/fixtureRunner.js +3 -3
- package/lib/worker/testInfo.js +8 -3
- package/lib/worker/timeoutManager.js +11 -5
- package/lib/worker/workerMain.js +6 -3
- package/package.json +2 -2
- package/types/component.d.ts +1 -1
- package/types/reporterPrivate.ts +22 -0
- package/types/test.d.ts +18 -22
- package/types/testReporter.d.ts +5 -1
- package/lib/common/profiler.js +0 -53
package/lib/runner/reporters.js
CHANGED
|
@@ -34,7 +34,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
34
34
|
* limitations under the License.
|
|
35
35
|
*/
|
|
36
36
|
|
|
37
|
-
async function createReporter(config, mode) {
|
|
37
|
+
async function createReporter(config, mode, additionalReporters = []) {
|
|
38
38
|
const defaultReporters = {
|
|
39
39
|
dot: mode === 'list' ? ListModeReporter : _dot.default,
|
|
40
40
|
line: mode === 'list' ? ListModeReporter : _line.default,
|
|
@@ -43,7 +43,7 @@ async function createReporter(config, mode) {
|
|
|
43
43
|
json: _json.default,
|
|
44
44
|
junit: _junit.default,
|
|
45
45
|
null: _empty.default,
|
|
46
|
-
html: _html.default
|
|
46
|
+
html: mode === 'ui' ? _line.default : _html.default
|
|
47
47
|
};
|
|
48
48
|
const reporters = [];
|
|
49
49
|
if (mode === 'watch') {
|
|
@@ -58,6 +58,7 @@ async function createReporter(config, mode) {
|
|
|
58
58
|
reporters.push(new reporterConstructor(arg));
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
|
+
reporters.push(...additionalReporters);
|
|
61
62
|
if (process.env.PW_TEST_REPORTER) {
|
|
62
63
|
const reporterConstructor = await (0, _loadUtils.loadReporter)(config, process.env.PW_TEST_REPORTER);
|
|
63
64
|
reporters.push(new reporterConstructor());
|
package/lib/runner/runner.js
CHANGED
|
@@ -11,6 +11,7 @@ var _reporters = require("./reporters");
|
|
|
11
11
|
var _tasks = require("./tasks");
|
|
12
12
|
var _utilsBundle = require("playwright-core/lib/utilsBundle");
|
|
13
13
|
var _watchMode = require("./watchMode");
|
|
14
|
+
var _uiMode = require("./uiMode");
|
|
14
15
|
/**
|
|
15
16
|
* Copyright 2019 Google Inc. All rights reserved.
|
|
16
17
|
* Modifications copyright (c) Microsoft Corporation.
|
|
@@ -57,7 +58,7 @@ class Runner {
|
|
|
57
58
|
factory: p
|
|
58
59
|
}));
|
|
59
60
|
const reporter = await (0, _reporters.createReporter)(config, listOnly ? 'list' : 'run');
|
|
60
|
-
const taskRunner = listOnly ? (0, _tasks.createTaskRunnerForList)(config, reporter) : (0, _tasks.createTaskRunner)(config, reporter);
|
|
61
|
+
const taskRunner = listOnly ? (0, _tasks.createTaskRunnerForList)(config, reporter, 'in-process') : (0, _tasks.createTaskRunner)(config, reporter);
|
|
61
62
|
const context = {
|
|
62
63
|
config,
|
|
63
64
|
reporter,
|
|
@@ -89,6 +90,13 @@ class Runner {
|
|
|
89
90
|
}));
|
|
90
91
|
return await (0, _watchMode.runWatchModeLoop)(config);
|
|
91
92
|
}
|
|
93
|
+
async uiAllTests() {
|
|
94
|
+
const config = this._config;
|
|
95
|
+
(0, _webServerPlugin.webServerPluginsForConfig)(config).forEach(p => config._internal.plugins.push({
|
|
96
|
+
factory: p
|
|
97
|
+
}));
|
|
98
|
+
return await (0, _uiMode.runUIMode)(config);
|
|
99
|
+
}
|
|
92
100
|
}
|
|
93
101
|
exports.Runner = Runner;
|
|
94
102
|
function sanitizeConfigForJSON(object, visited) {
|
package/lib/runner/taskRunner.js
CHANGED
|
@@ -41,18 +41,15 @@ class TaskRunner {
|
|
|
41
41
|
task
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
|
-
|
|
45
|
-
this._interrupted = true;
|
|
46
|
-
}
|
|
47
|
-
async run(context, deadline) {
|
|
44
|
+
async run(context, deadline, cancelPromise) {
|
|
48
45
|
const {
|
|
49
46
|
status,
|
|
50
47
|
cleanup
|
|
51
|
-
} = await this.runDeferCleanup(context, deadline);
|
|
48
|
+
} = await this.runDeferCleanup(context, deadline, cancelPromise);
|
|
52
49
|
const teardownStatus = await cleanup();
|
|
53
50
|
return status === 'passed' ? teardownStatus : status;
|
|
54
51
|
}
|
|
55
|
-
async runDeferCleanup(context, deadline) {
|
|
52
|
+
async runDeferCleanup(context, deadline, cancelPromise = new _utils.ManualPromise()) {
|
|
56
53
|
const sigintWatcher = new _sigIntWatcher.SigIntWatcher();
|
|
57
54
|
const timeoutWatcher = new TimeoutWatcher(deadline);
|
|
58
55
|
const teardownRunner = new TaskRunner(this._reporter, this._globalTimeoutForError);
|
|
@@ -89,14 +86,14 @@ class TaskRunner {
|
|
|
89
86
|
(0, _utilsBundle.debug)('pw:test:task')(`"${name}" finished`);
|
|
90
87
|
}
|
|
91
88
|
};
|
|
92
|
-
await Promise.race([taskLoop(), sigintWatcher.promise(), timeoutWatcher.promise]);
|
|
89
|
+
await Promise.race([taskLoop(), cancelPromise, sigintWatcher.promise(), timeoutWatcher.promise]);
|
|
93
90
|
sigintWatcher.disarm();
|
|
94
91
|
timeoutWatcher.disarm();
|
|
95
92
|
|
|
96
93
|
// Prevent subsequent tasks from running.
|
|
97
94
|
this._interrupted = true;
|
|
98
95
|
let status = 'passed';
|
|
99
|
-
if (sigintWatcher.hadSignal()) {
|
|
96
|
+
if (sigintWatcher.hadSignal() || cancelPromise !== null && cancelPromise !== void 0 && cancelPromise.isDone()) {
|
|
100
97
|
status = 'interrupted';
|
|
101
98
|
} else if (timeoutWatcher.timedOut()) {
|
|
102
99
|
var _this$_reporter$onErr2, _this$_reporter2;
|
|
@@ -107,6 +104,7 @@ class TaskRunner {
|
|
|
107
104
|
} else if (this._hasErrors) {
|
|
108
105
|
status = 'failed';
|
|
109
106
|
}
|
|
107
|
+
cancelPromise === null || cancelPromise === void 0 ? void 0 : cancelPromise.resolve();
|
|
110
108
|
const cleanup = () => teardownRunner.runDeferCleanup(context, deadline).then(r => r.status);
|
|
111
109
|
return {
|
|
112
110
|
status,
|
package/lib/runner/tasks.js
CHANGED
|
@@ -15,7 +15,6 @@ var _dispatcher = require("./dispatcher");
|
|
|
15
15
|
var _testGroups = require("../runner/testGroups");
|
|
16
16
|
var _taskRunner = require("./taskRunner");
|
|
17
17
|
var _loadUtils = require("./loadUtils");
|
|
18
|
-
var _util2 = require("../util");
|
|
19
18
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
20
19
|
/**
|
|
21
20
|
* Copyright Microsoft Corporation. All rights reserved.
|
|
@@ -59,7 +58,7 @@ function addGlobalSetupTasks(taskRunner, config) {
|
|
|
59
58
|
taskRunner.addTask('clear output', createRemoveOutputDirsTask());
|
|
60
59
|
}
|
|
61
60
|
function addRunTasks(taskRunner, config) {
|
|
62
|
-
taskRunner.addTask('create
|
|
61
|
+
taskRunner.addTask('create phases', createPhasesTask());
|
|
63
62
|
taskRunner.addTask('report begin', async ({
|
|
64
63
|
reporter,
|
|
65
64
|
rootSuite
|
|
@@ -73,9 +72,9 @@ function addRunTasks(taskRunner, config) {
|
|
|
73
72
|
taskRunner.addTask('test suite', createRunTestsTask());
|
|
74
73
|
return taskRunner;
|
|
75
74
|
}
|
|
76
|
-
function createTaskRunnerForList(config, reporter) {
|
|
75
|
+
function createTaskRunnerForList(config, reporter, mode) {
|
|
77
76
|
const taskRunner = new _taskRunner.TaskRunner(reporter, config.globalTimeout);
|
|
78
|
-
taskRunner.addTask('load tests', createLoadTask(
|
|
77
|
+
taskRunner.addTask('load tests', createLoadTask(mode, false));
|
|
79
78
|
taskRunner.addTask('report begin', async ({
|
|
80
79
|
reporter,
|
|
81
80
|
rootSuite
|
|
@@ -151,39 +150,49 @@ function createLoadTask(mode, shouldFilterOnly, projectsToIgnore = new Set(), ad
|
|
|
151
150
|
const {
|
|
152
151
|
config
|
|
153
152
|
} = context;
|
|
154
|
-
const
|
|
155
|
-
const
|
|
156
|
-
context.rootSuite = await (0, _loadUtils.
|
|
153
|
+
const filesToRunByProject = await (0, _loadUtils.collectProjectsAndTestFiles)(config, projectsToIgnore, additionalFileMatcher);
|
|
154
|
+
const fileSuitesByProject = await (0, _loadUtils.loadFileSuites)(mode, config, filesToRunByProject, errors);
|
|
155
|
+
context.rootSuite = await (0, _loadUtils.createRootSuite)(config, fileSuitesByProject, errors, shouldFilterOnly);
|
|
157
156
|
// Fail when no tests.
|
|
158
157
|
if (!context.rootSuite.allTests().length && !config._internal.passWithNoTests && !config.shard) throw new Error(`No tests found`);
|
|
159
158
|
};
|
|
160
159
|
}
|
|
161
|
-
function
|
|
160
|
+
function createPhasesTask() {
|
|
162
161
|
return async context => {
|
|
163
|
-
const {
|
|
164
|
-
config,
|
|
165
|
-
rootSuite,
|
|
166
|
-
reporter
|
|
167
|
-
} = context;
|
|
168
162
|
context.config._internal.maxConcurrentTestGroups = 0;
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
163
|
+
const processed = new Set();
|
|
164
|
+
const projectToSuite = new Map(context.rootSuite.suites.map(suite => [suite.project(), suite]));
|
|
165
|
+
for (let i = 0; i < projectToSuite.size; i++) {
|
|
166
|
+
// Find all projects that have all their dependencies processed by previous phases.
|
|
167
|
+
const phaseProjects = [];
|
|
168
|
+
for (const project of projectToSuite.keys()) {
|
|
169
|
+
if (processed.has(project)) continue;
|
|
170
|
+
if (project._internal.deps.find(p => !processed.has(p))) continue;
|
|
171
|
+
phaseProjects.push(project);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Create a new phase.
|
|
175
|
+
for (const project of phaseProjects) processed.add(project);
|
|
176
|
+
if (phaseProjects.length) {
|
|
177
|
+
let testGroupsInPhase = 0;
|
|
178
|
+
const phase = {
|
|
179
|
+
dispatcher: new _dispatcher.Dispatcher(context.config, context.reporter),
|
|
180
|
+
projects: []
|
|
181
|
+
};
|
|
182
|
+
context.phases.push(phase);
|
|
183
|
+
for (const project of phaseProjects) {
|
|
184
|
+
const projectSuite = projectToSuite.get(project);
|
|
185
|
+
const testGroups = (0, _testGroups.createTestGroups)(projectSuite, context.config.workers);
|
|
186
|
+
phase.projects.push({
|
|
187
|
+
project,
|
|
188
|
+
projectSuite,
|
|
189
|
+
testGroups
|
|
190
|
+
});
|
|
191
|
+
testGroupsInPhase += testGroups.length;
|
|
192
|
+
}
|
|
193
|
+
(0, _utilsBundle.debug)('pw:test:task')(`created phase #${context.phases.length} with ${phase.projects.map(p => p.project.name).sort()} projects, ${testGroupsInPhase} testGroups`);
|
|
194
|
+
context.config._internal.maxConcurrentTestGroups = Math.max(context.config._internal.maxConcurrentTestGroups, testGroupsInPhase);
|
|
179
195
|
}
|
|
180
|
-
const testGroupsInPhase = projects.reduce((acc, project) => acc + project.testGroups.length, 0);
|
|
181
|
-
(0, _utilsBundle.debug)('pw:test:task')(`running phase with ${projects.map(p => p.project.name).sort()} projects, ${testGroupsInPhase} testGroups`);
|
|
182
|
-
context.phases.push({
|
|
183
|
-
dispatcher: new _dispatcher.Dispatcher(config, reporter),
|
|
184
|
-
projects
|
|
185
|
-
});
|
|
186
|
-
context.config._internal.maxConcurrentTestGroups = Math.max(context.config._internal.maxConcurrentTestGroups, testGroupsInPhase);
|
|
187
196
|
}
|
|
188
197
|
};
|
|
189
198
|
}
|
|
@@ -204,6 +213,7 @@ function createRunTestsTask() {
|
|
|
204
213
|
phases
|
|
205
214
|
} = context;
|
|
206
215
|
const successfulProjects = new Set();
|
|
216
|
+
const extraEnvByProjectId = new Map();
|
|
207
217
|
for (const {
|
|
208
218
|
dispatcher,
|
|
209
219
|
projects
|
|
@@ -216,6 +226,13 @@ function createRunTestsTask() {
|
|
|
216
226
|
project,
|
|
217
227
|
testGroups
|
|
218
228
|
} of projects) {
|
|
229
|
+
// Inherit extra enviroment variables from dependencies.
|
|
230
|
+
let extraEnv = {};
|
|
231
|
+
for (const dep of project._internal.deps) extraEnv = {
|
|
232
|
+
...extraEnv,
|
|
233
|
+
...extraEnvByProjectId.get(dep._internal.id)
|
|
234
|
+
};
|
|
235
|
+
extraEnvByProjectId.set(project._internal.id, extraEnv);
|
|
219
236
|
const hasFailedDeps = project._internal.deps.some(p => !successfulProjects.has(p));
|
|
220
237
|
if (!hasFailedDeps) {
|
|
221
238
|
phaseTestGroups.push(...testGroups);
|
|
@@ -226,8 +243,15 @@ function createRunTestsTask() {
|
|
|
226
243
|
}
|
|
227
244
|
}
|
|
228
245
|
if (phaseTestGroups.length) {
|
|
229
|
-
await dispatcher.run(phaseTestGroups);
|
|
246
|
+
await dispatcher.run(phaseTestGroups, extraEnvByProjectId);
|
|
230
247
|
await dispatcher.stop();
|
|
248
|
+
for (const [projectId, envProduced] of dispatcher.producedEnvByProjectId()) {
|
|
249
|
+
const extraEnv = extraEnvByProjectId.get(projectId) || {};
|
|
250
|
+
extraEnvByProjectId.set(projectId, {
|
|
251
|
+
...extraEnv,
|
|
252
|
+
...envProduced
|
|
253
|
+
});
|
|
254
|
+
}
|
|
231
255
|
}
|
|
232
256
|
|
|
233
257
|
// If the worker broke, fail everything, we have no way of knowing which
|
|
@@ -243,19 +267,4 @@ function createRunTestsTask() {
|
|
|
243
267
|
}
|
|
244
268
|
}
|
|
245
269
|
};
|
|
246
|
-
}
|
|
247
|
-
function buildPhases(projectSuites) {
|
|
248
|
-
const phases = [];
|
|
249
|
-
const processed = new Set();
|
|
250
|
-
for (let i = 0; i < projectSuites.length; i++) {
|
|
251
|
-
const phase = [];
|
|
252
|
-
for (const projectSuite of projectSuites) {
|
|
253
|
-
if (processed.has(projectSuite._projectConfig)) continue;
|
|
254
|
-
if (projectSuite._projectConfig._internal.deps.find(p => !processed.has(p))) continue;
|
|
255
|
-
phase.push(projectSuite);
|
|
256
|
-
}
|
|
257
|
-
for (const projectSuite of phase) processed.add(projectSuite._projectConfig);
|
|
258
|
-
if (phase.length) phases.push(phase);
|
|
259
|
-
}
|
|
260
|
-
return phases;
|
|
261
270
|
}
|
package/lib/runner/testGroups.js
CHANGED
|
@@ -106,9 +106,16 @@ function createTestGroups(projectSuite, workers) {
|
|
|
106
106
|
}
|
|
107
107
|
return result;
|
|
108
108
|
}
|
|
109
|
-
function filterForShard(shard,
|
|
109
|
+
function filterForShard(shard, testGroups) {
|
|
110
|
+
// Note that sharding works based on test groups.
|
|
111
|
+
// This means parallel files will be sharded by single tests,
|
|
112
|
+
// while non-parallel files will be sharded by the whole file.
|
|
113
|
+
//
|
|
114
|
+
// Shards are still balanced by the number of tests, not files,
|
|
115
|
+
// even in the case of non-paralleled files.
|
|
116
|
+
|
|
110
117
|
let shardableTotal = 0;
|
|
111
|
-
for (const
|
|
118
|
+
for (const group of testGroups) shardableTotal += group.tests.length;
|
|
112
119
|
|
|
113
120
|
// Each shard gets some tests.
|
|
114
121
|
const shardSize = Math.floor(shardableTotal / shard.total);
|
|
@@ -118,14 +125,12 @@ function filterForShard(shard, filesByProject) {
|
|
|
118
125
|
const from = shardSize * currentShard + Math.min(extraOne, currentShard);
|
|
119
126
|
const to = from + shardSize + (currentShard < extraOne ? 1 : 0);
|
|
120
127
|
let current = 0;
|
|
121
|
-
const result = new
|
|
122
|
-
for (const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
if (shardFiles.length) result.set(project, shardFiles);
|
|
128
|
+
const result = new Set();
|
|
129
|
+
for (const group of testGroups) {
|
|
130
|
+
// Any test group goes to the shard that contains the first test of this group.
|
|
131
|
+
// So, this shard gets any group that starts at [from; to)
|
|
132
|
+
if (current >= from && current < to) result.add(group);
|
|
133
|
+
current += group.tests.length;
|
|
129
134
|
}
|
|
130
135
|
return result;
|
|
131
136
|
}
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.runUIMode = runUIMode;
|
|
7
|
+
var _server = require("playwright-core/lib/server");
|
|
8
|
+
var _utils = require("playwright-core/lib/utils");
|
|
9
|
+
var _compilationCache = require("../common/compilationCache");
|
|
10
|
+
var _multiplexer = require("../reporters/multiplexer");
|
|
11
|
+
var _teleEmitter = require("../reporters/teleEmitter");
|
|
12
|
+
var _reporters = require("./reporters");
|
|
13
|
+
var _tasks = require("./tasks");
|
|
14
|
+
var _utilsBundle = require("../utilsBundle");
|
|
15
|
+
/**
|
|
16
|
+
* Copyright Microsoft Corporation. All rights reserved.
|
|
17
|
+
*
|
|
18
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
19
|
+
* you may not use this file except in compliance with the License.
|
|
20
|
+
* You may obtain a copy of the License at
|
|
21
|
+
*
|
|
22
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
23
|
+
*
|
|
24
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
25
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
26
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
27
|
+
* See the License for the specific language governing permissions and
|
|
28
|
+
* limitations under the License.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
class UIMode {
|
|
32
|
+
constructor(config) {
|
|
33
|
+
this._config = void 0;
|
|
34
|
+
this._page = void 0;
|
|
35
|
+
this._testRun = void 0;
|
|
36
|
+
this.globalCleanup = void 0;
|
|
37
|
+
this._testWatcher = void 0;
|
|
38
|
+
this._originalStderr = void 0;
|
|
39
|
+
this._config = config;
|
|
40
|
+
process.env.PW_LIVE_TRACE_STACKS = '1';
|
|
41
|
+
config._internal.configCLIOverrides.forbidOnly = false;
|
|
42
|
+
config._internal.configCLIOverrides.globalTimeout = 0;
|
|
43
|
+
config._internal.configCLIOverrides.repeatEach = 0;
|
|
44
|
+
config._internal.configCLIOverrides.shard = undefined;
|
|
45
|
+
config._internal.configCLIOverrides.updateSnapshots = undefined;
|
|
46
|
+
config._internal.listOnly = false;
|
|
47
|
+
config._internal.passWithNoTests = true;
|
|
48
|
+
for (const project of config.projects) project._internal.deps = [];
|
|
49
|
+
for (const p of config.projects) p.retries = 0;
|
|
50
|
+
config._internal.configCLIOverrides.use = config._internal.configCLIOverrides.use || {};
|
|
51
|
+
config._internal.configCLIOverrides.use.trace = {
|
|
52
|
+
mode: 'on',
|
|
53
|
+
sources: false
|
|
54
|
+
};
|
|
55
|
+
this._originalStderr = process.stderr.write.bind(process.stderr);
|
|
56
|
+
this._installGlobalWatcher();
|
|
57
|
+
}
|
|
58
|
+
_installGlobalWatcher() {
|
|
59
|
+
const projectDirs = new Set();
|
|
60
|
+
for (const p of this._config.projects) projectDirs.add(p.testDir);
|
|
61
|
+
let coalescingTimer;
|
|
62
|
+
const watcher = _utilsBundle.chokidar.watch([...projectDirs], {
|
|
63
|
+
ignoreInitial: true,
|
|
64
|
+
persistent: true
|
|
65
|
+
}).on('all', async event => {
|
|
66
|
+
if (event !== 'add' && event !== 'change' && event !== 'unlink') return;
|
|
67
|
+
if (coalescingTimer) clearTimeout(coalescingTimer);
|
|
68
|
+
coalescingTimer = setTimeout(() => {
|
|
69
|
+
this._dispatchEvent({
|
|
70
|
+
method: 'listChanged'
|
|
71
|
+
});
|
|
72
|
+
}, 200);
|
|
73
|
+
});
|
|
74
|
+
return watcher;
|
|
75
|
+
}
|
|
76
|
+
async runGlobalSetup() {
|
|
77
|
+
const reporter = await (0, _reporters.createReporter)(this._config, 'watch');
|
|
78
|
+
const taskRunner = (0, _tasks.createTaskRunnerForWatchSetup)(this._config, reporter);
|
|
79
|
+
reporter.onConfigure(this._config);
|
|
80
|
+
const context = {
|
|
81
|
+
config: this._config,
|
|
82
|
+
reporter,
|
|
83
|
+
phases: []
|
|
84
|
+
};
|
|
85
|
+
const {
|
|
86
|
+
status,
|
|
87
|
+
cleanup: globalCleanup
|
|
88
|
+
} = await taskRunner.runDeferCleanup(context, 0);
|
|
89
|
+
if (status !== 'passed') {
|
|
90
|
+
await globalCleanup();
|
|
91
|
+
return status;
|
|
92
|
+
}
|
|
93
|
+
this.globalCleanup = globalCleanup;
|
|
94
|
+
return status;
|
|
95
|
+
}
|
|
96
|
+
async showUI() {
|
|
97
|
+
this._page = await (0, _server.showTraceViewer)([], 'chromium', {
|
|
98
|
+
app: 'watch.html',
|
|
99
|
+
headless: (0, _utils.isUnderTest)() && process.env.PWTEST_HEADED_FOR_TEST !== '1'
|
|
100
|
+
});
|
|
101
|
+
if (!process.env.PWTEST_DEBUG) {
|
|
102
|
+
process.stdout.write = chunk => {
|
|
103
|
+
this._dispatchEvent({
|
|
104
|
+
method: 'stdio',
|
|
105
|
+
params: chunkToPayload('stdout', chunk)
|
|
106
|
+
});
|
|
107
|
+
return true;
|
|
108
|
+
};
|
|
109
|
+
process.stderr.write = chunk => {
|
|
110
|
+
this._dispatchEvent({
|
|
111
|
+
method: 'stdio',
|
|
112
|
+
params: chunkToPayload('stderr', chunk)
|
|
113
|
+
});
|
|
114
|
+
return true;
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
const exitPromise = new _utils.ManualPromise();
|
|
118
|
+
this._page.on('close', () => exitPromise.resolve());
|
|
119
|
+
let queue = Promise.resolve();
|
|
120
|
+
this._page.exposeBinding('sendMessage', false, async (source, data) => {
|
|
121
|
+
const {
|
|
122
|
+
method,
|
|
123
|
+
params
|
|
124
|
+
} = data;
|
|
125
|
+
if (method === 'exit') {
|
|
126
|
+
exitPromise.resolve();
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (method === 'watch') {
|
|
130
|
+
this._watchFiles(params.fileNames);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (method === 'open' && params.location) {
|
|
134
|
+
(0, _utilsBundle.open)('vscode://file/' + params.location).catch(e => this._originalStderr(String(e)));
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (method === 'resizeTerminal') {
|
|
138
|
+
process.stdout.columns = params.cols;
|
|
139
|
+
process.stdout.rows = params.rows;
|
|
140
|
+
process.stderr.columns = params.cols;
|
|
141
|
+
process.stderr.columns = params.rows;
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (method === 'stop') {
|
|
145
|
+
this._stopTests();
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
queue = queue.then(() => this._queueListOrRun(method, params));
|
|
149
|
+
await queue;
|
|
150
|
+
});
|
|
151
|
+
await exitPromise;
|
|
152
|
+
}
|
|
153
|
+
async _queueListOrRun(method, params) {
|
|
154
|
+
if (method === 'list') await this._listTests();
|
|
155
|
+
if (method === 'run') await this._runTests(params.testIds);
|
|
156
|
+
}
|
|
157
|
+
_dispatchEvent(message) {
|
|
158
|
+
// eslint-disable-next-line no-console
|
|
159
|
+
this._page.mainFrame().evaluateExpression(dispatchFuncSource, true, message).catch(e => this._originalStderr(String(e)));
|
|
160
|
+
}
|
|
161
|
+
async _listTests() {
|
|
162
|
+
const listReporter = new _teleEmitter.TeleReporterEmitter(e => this._dispatchEvent(e));
|
|
163
|
+
const reporter = new _multiplexer.Multiplexer([listReporter]);
|
|
164
|
+
this._config._internal.listOnly = true;
|
|
165
|
+
this._config._internal.testIdMatcher = undefined;
|
|
166
|
+
const taskRunner = (0, _tasks.createTaskRunnerForList)(this._config, reporter, 'out-of-process');
|
|
167
|
+
const context = {
|
|
168
|
+
config: this._config,
|
|
169
|
+
reporter,
|
|
170
|
+
phases: []
|
|
171
|
+
};
|
|
172
|
+
(0, _compilationCache.clearCompilationCache)();
|
|
173
|
+
reporter.onConfigure(this._config);
|
|
174
|
+
const status = await taskRunner.run(context, 0);
|
|
175
|
+
reporter.onExit({
|
|
176
|
+
status
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
async _runTests(testIds) {
|
|
180
|
+
await this._stopTests();
|
|
181
|
+
const testIdSet = testIds ? new Set(testIds) : null;
|
|
182
|
+
this._config._internal.listOnly = false;
|
|
183
|
+
this._config._internal.testIdMatcher = id => !testIdSet || testIdSet.has(id);
|
|
184
|
+
const runReporter = new _teleEmitter.TeleReporterEmitter(e => this._dispatchEvent(e));
|
|
185
|
+
const reporter = await (0, _reporters.createReporter)(this._config, 'ui', [runReporter]);
|
|
186
|
+
const taskRunner = (0, _tasks.createTaskRunnerForWatch)(this._config, reporter);
|
|
187
|
+
const context = {
|
|
188
|
+
config: this._config,
|
|
189
|
+
reporter,
|
|
190
|
+
phases: []
|
|
191
|
+
};
|
|
192
|
+
(0, _compilationCache.clearCompilationCache)();
|
|
193
|
+
reporter.onConfigure(this._config);
|
|
194
|
+
const stop = new _utils.ManualPromise();
|
|
195
|
+
const run = taskRunner.run(context, 0, stop).then(async status => {
|
|
196
|
+
await reporter.onExit({
|
|
197
|
+
status
|
|
198
|
+
});
|
|
199
|
+
this._testRun = undefined;
|
|
200
|
+
this._config._internal.testIdMatcher = undefined;
|
|
201
|
+
return status;
|
|
202
|
+
});
|
|
203
|
+
this._testRun = {
|
|
204
|
+
run,
|
|
205
|
+
stop
|
|
206
|
+
};
|
|
207
|
+
await run;
|
|
208
|
+
}
|
|
209
|
+
async _watchFiles(fileNames) {
|
|
210
|
+
const files = new Set();
|
|
211
|
+
for (const fileName of fileNames) {
|
|
212
|
+
files.add(fileName);
|
|
213
|
+
(0, _compilationCache.dependenciesForTestFile)(fileName).forEach(file => files.add(file));
|
|
214
|
+
}
|
|
215
|
+
const watchedFiles = [...files].sort();
|
|
216
|
+
if (this._testWatcher && JSON.stringify(this._testWatcher.watchedFiles) === JSON.stringify(watchedFiles)) return;
|
|
217
|
+
if (this._testWatcher) {
|
|
218
|
+
if (this._testWatcher.collector.size) this._dispatchEvent({
|
|
219
|
+
method: 'filesChanged',
|
|
220
|
+
params: {
|
|
221
|
+
fileNames: [...this._testWatcher.collector]
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
clearTimeout(this._testWatcher.timer);
|
|
225
|
+
this._testWatcher.watcher.close().then(() => {});
|
|
226
|
+
this._testWatcher = undefined;
|
|
227
|
+
}
|
|
228
|
+
if (!watchedFiles.length) return;
|
|
229
|
+
const collector = new Set();
|
|
230
|
+
const watcher = _utilsBundle.chokidar.watch(watchedFiles, {
|
|
231
|
+
ignoreInitial: true
|
|
232
|
+
}).on('all', async (event, file) => {
|
|
233
|
+
if (event !== 'add' && event !== 'change') return;
|
|
234
|
+
(0, _compilationCache.collectAffectedTestFiles)(file, collector);
|
|
235
|
+
if (this._testWatcher.timer) clearTimeout(this._testWatcher.timer);
|
|
236
|
+
this._testWatcher.timer = setTimeout(() => {
|
|
237
|
+
const fileNames = [...collector];
|
|
238
|
+
collector.clear();
|
|
239
|
+
this._dispatchEvent({
|
|
240
|
+
method: 'filesChanged',
|
|
241
|
+
params: {
|
|
242
|
+
fileNames
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
}, 250);
|
|
246
|
+
});
|
|
247
|
+
this._testWatcher = {
|
|
248
|
+
watcher,
|
|
249
|
+
watchedFiles,
|
|
250
|
+
collector
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
async _stopTests() {
|
|
254
|
+
var _this$_testRun, _this$_testRun$stop, _this$_testRun2;
|
|
255
|
+
(_this$_testRun = this._testRun) === null || _this$_testRun === void 0 ? void 0 : (_this$_testRun$stop = _this$_testRun.stop) === null || _this$_testRun$stop === void 0 ? void 0 : _this$_testRun$stop.resolve();
|
|
256
|
+
await ((_this$_testRun2 = this._testRun) === null || _this$_testRun2 === void 0 ? void 0 : _this$_testRun2.run);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
const dispatchFuncSource = String(message => {
|
|
260
|
+
window.dispatch(message);
|
|
261
|
+
});
|
|
262
|
+
async function runUIMode(config) {
|
|
263
|
+
var _uiMode$globalCleanup;
|
|
264
|
+
const uiMode = new UIMode(config);
|
|
265
|
+
const status = await uiMode.runGlobalSetup();
|
|
266
|
+
if (status !== 'passed') return status;
|
|
267
|
+
await uiMode.showUI();
|
|
268
|
+
return (await ((_uiMode$globalCleanup = uiMode.globalCleanup) === null || _uiMode$globalCleanup === void 0 ? void 0 : _uiMode$globalCleanup.call(uiMode))) || 'passed';
|
|
269
|
+
}
|
|
270
|
+
function chunkToPayload(type, chunk) {
|
|
271
|
+
if (chunk instanceof Buffer) return {
|
|
272
|
+
type,
|
|
273
|
+
buffer: chunk.toString('base64')
|
|
274
|
+
};
|
|
275
|
+
return {
|
|
276
|
+
type,
|
|
277
|
+
text: chunk
|
|
278
|
+
};
|
|
279
|
+
}
|
package/lib/runner/workerHost.js
CHANGED
|
@@ -23,9 +23,13 @@ var _processHost = require("./processHost");
|
|
|
23
23
|
|
|
24
24
|
let lastWorkerIndex = 0;
|
|
25
25
|
class WorkerHost extends _processHost.ProcessHost {
|
|
26
|
-
constructor(testGroup, parallelIndex, config) {
|
|
26
|
+
constructor(testGroup, parallelIndex, config, extraEnv) {
|
|
27
27
|
const workerIndex = lastWorkerIndex++;
|
|
28
|
-
super(require.resolve('../worker/workerMain.js'), `worker-${workerIndex}
|
|
28
|
+
super(require.resolve('../worker/workerMain.js'), `worker-${workerIndex}`, {
|
|
29
|
+
...extraEnv,
|
|
30
|
+
FORCE_COLOR: '1',
|
|
31
|
+
DEBUG_COLORS: '1'
|
|
32
|
+
});
|
|
29
33
|
this.parallelIndex = void 0;
|
|
30
34
|
this.workerIndex = void 0;
|
|
31
35
|
this._hash = void 0;
|
|
@@ -43,10 +47,7 @@ class WorkerHost extends _processHost.ProcessHost {
|
|
|
43
47
|
};
|
|
44
48
|
}
|
|
45
49
|
async start() {
|
|
46
|
-
await this.startRunner(this._params, false
|
|
47
|
-
FORCE_COLOR: '1',
|
|
48
|
-
DEBUG_COLORS: '1'
|
|
49
|
-
});
|
|
50
|
+
await this.startRunner(this._params, false);
|
|
50
51
|
}
|
|
51
52
|
runTestGroup(runPayload) {
|
|
52
53
|
this.sendMessageNoReply({
|