From 53ac35a613d53ead1e82a65ae89d72d1a8de91cf Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Sun, 23 Aug 2020 11:44:41 -0700 Subject: [PATCH] chore(testrunner): complete ts migration (#3587) --- package-lock.json | 58 +++++++++++++++++ package.json | 2 + test-runner/src/cli.js | 17 ----- test-runner/src/expect.ts | 15 ++--- test-runner/src/fixtures.ts | 55 ++++++++-------- test-runner/src/{GoldenUtils.js => golden.ts} | 62 ++++++------------- test-runner/src/index.ts | 15 ++++- test-runner/src/runner.ts | 54 +++++++--------- test-runner/src/test.ts | 3 +- test-runner/src/testCollector.ts | 27 ++++---- test-runner/src/testRunner.ts | 13 ++-- .../src/{transform.js => transform.ts} | 30 ++++----- 12 files changed, 180 insertions(+), 171 deletions(-) delete mode 100644 test-runner/src/cli.js rename test-runner/src/{GoldenUtils.js => golden.ts} (76%) rename test-runner/src/{transform.js => transform.ts} (80%) diff --git a/package-lock.json b/package-lock.json index f5d66d291b..7176f42a8c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1168,6 +1168,47 @@ "defer-to-connect": "^1.0.1" } }, + "@types/babel__core": { + "version": "7.1.9", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.9.tgz", + "integrity": "sha512-sY2RsIJ5rpER1u3/aQ8OFSI7qGIy8o1NEEbgb2UaJcvOtXOMpd39ko723NBpjQFg9SIX7TXtjejZVGeIMLhoOw==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.1.tgz", + "integrity": "sha512-bBKm+2VPJcMRVwNhxKu8W+5/zT7pwNEqeokFOmbvVSqGzFneNxYcEBro9Ac7/N9tlsaPYnZLK8J1LWKkMsLAew==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz", + "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.13.tgz", + "integrity": "sha512-i+zS7t6/s9cdQvbqKDARrcbrPvtJGlbYsMkazo03nTAK3RX9FNrLllXys22uiTGJapPOTZTQ35nHh4ISph4SLQ==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", @@ -1287,6 +1328,23 @@ "@types/node": "*" } }, + "@types/source-map-support": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@types/source-map-support/-/source-map-support-0.5.3.tgz", + "integrity": "sha512-fvjMjVH8Rmokw2dWh1dkj90iX5R8FPjeZzjNH+6eFXReh0QnHFf1YBl3B0CF0RohIAA3SDRJsGeeUWKl6d7HqA==", + "dev": true, + "requires": { + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", diff --git a/package.json b/package.json index 8efd3cd2e2..731514b022 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "@babel/core": "^7.10.3", "@babel/preset-env": "^7.10.3", "@babel/preset-typescript": "^7.10.1", + "@types/babel__core": "^7.1.9", "@types/debug": "0.0.31", "@types/extract-zip": "^1.6.2", "@types/mime": "^2.0.1", @@ -63,6 +64,7 @@ "@types/progress": "^2.0.3", "@types/proxy-from-env": "^1.0.0", "@types/rimraf": "^2.0.2", + "@types/source-map-support": "^0.5.3", "@types/stack-utils": "^1.0.1", "@types/ws": "^6.0.1", "@typescript-eslint/eslint-plugin": "^2.6.1", diff --git a/test-runner/src/cli.js b/test-runner/src/cli.js deleted file mode 100644 index 44f379da46..0000000000 --- a/test-runner/src/cli.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * 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. - */ - -require('./lib/cli'); diff --git a/test-runner/src/expect.ts b/test-runner/src/expect.ts index 478c771666..f60c33e215 100644 --- a/test-runner/src/expect.ts +++ b/test-runner/src/expect.ts @@ -14,7 +14,8 @@ * limitations under the License. */ -import GoldenUtils from './GoldenUtils'; +import { compare } from './golden'; +import { RunnerConfig } from './runnerConfig'; declare global { const expect: typeof import('expect'); @@ -28,16 +29,16 @@ declare module 'expect/build/types' { global['expect'] = require('expect'); -let relativeTestFile: string; +let testFile: string; -export function initializeImageMatcher(options) { - function toMatchImage(received, name, config) { - const { pass, message } = GoldenUtils.compare(received, name, { ...options, relativeTestFile, config }); +export function initializeImageMatcher(config: RunnerConfig) { + function toMatchImage(received: Buffer, name: string, options?: { threshold?: number }) { + const { pass, message } = compare(received, name, config, testFile, options); return { pass, message: () => message }; }; expect.extend({ toMatchImage }); } -export function setCurrentTestFile(testFile: string) { - relativeTestFile = testFile; +export function setCurrentTestFile(file: string) { + testFile = file; } \ No newline at end of file diff --git a/test-runner/src/fixtures.ts b/test-runner/src/fixtures.ts index edbcd2bfdc..b83e001506 100644 --- a/test-runner/src/fixtures.ts +++ b/test-runner/src/fixtures.ts @@ -17,32 +17,29 @@ import debug from 'debug'; import { Test } from './test'; -declare global { - interface WorkerState { - } +type Scope = 'test' | 'worker'; - interface TestState { - } +type FixtureRegistration = { + name: string; + scope: Scope; + fn: Function; +}; - interface FixtureParameters { - } -} - -const registrations = new Map(); -const registrationsByFile = new Map(); -export let parameters: FixtureParameters = {} as FixtureParameters; +const registrations = new Map(); +const registrationsByFile = new Map(); +export let parameters: any = {}; export const parameterRegistrations = new Map(); export function setParameters(params: any) { parameters = Object.assign(parameters, params); for (const name of Object.keys(params)) - registerWorkerFixture(name as keyof WorkerState, async ({}, test) => await test(parameters[name] as never)); + registerWorkerFixture(name, async ({}, test) => await test(parameters[name])); } class Fixture { pool: FixturePool; name: string; - scope: string; + scope: Scope; fn: Function; deps: string[]; usages: Set; @@ -53,7 +50,7 @@ class Fixture { _setup = false; _teardown = false; - constructor(pool: FixturePool, name: string, scope: string, fn: any) { + constructor(pool: FixturePool, name: string, scope: Scope, fn: any) { this.pool = pool; this.name = name; this.scope = scope; @@ -137,7 +134,7 @@ export class FixturePool { } } - async resolveParametersAndRun(fn: (arg0: {}) => any, timeout: number, config: Config, test?: Test) { + async resolveParametersAndRun(fn: Function, timeout: number, config: Config, test?: Test) { const names = fixtureParameterNames(fn); for (const name of names) await this.setupFixture(name, config, test); @@ -148,7 +145,7 @@ export class FixturePool { if (!timeout) return fn(params); - let timer; + let timer: NodeJS.Timer; let timerPromise = new Promise(f => timer = setTimeout(f, timeout)); return Promise.race([ Promise.resolve(fn(params)).then(() => clearTimeout(timer)), @@ -169,9 +166,9 @@ export class FixturePool { } } -export function fixturesForCallback(callback: any): string[] { +export function fixturesForCallback(callback: Function): string[] { const names = new Set(); - const visit = (callback: any) => { + const visit = (callback: Function) => { for (const name of fixtureParameterNames(callback)) { if (name in names) continue; @@ -189,7 +186,7 @@ export function fixturesForCallback(callback: any): string[] { return result; } -function fixtureParameterNames(fn: { toString: () => any; }): string[] { +function fixtureParameterNames(fn: Function): string[] { const text = fn.toString(); const match = text.match(/async(?:\s+function)?\s*\(\s*{\s*([^}]*)\s*}/); if (!match || !match[1].trim()) @@ -198,7 +195,7 @@ function fixtureParameterNames(fn: { toString: () => any; }): string[] { return signature.split(',').map((t: string) => t.trim()); } -function innerRegisterFixture(name: string, scope: string, fn: Function, caller: Function) { +function innerRegisterFixture(name: string, scope: Scope, fn: Function, caller: Function) { const obj = {stack: ''}; Error.captureStackTrace(obj, caller); const stackFrame = obj.stack.split('\n')[2]; @@ -211,20 +208,20 @@ function innerRegisterFixture(name: string, scope: string, fn: Function, caller: registrationsByFile.get(file).push(registration); }; -export function registerFixture(name: T, fn: (params: FixtureParameters & WorkerState & TestState, runTest: (arg: TestState[T]) => Promise, config: Config, test: Test) => Promise) { +export function registerFixture(name: string, fn: (params: any, runTest: (arg: any) => Promise, config: Config, test: Test) => Promise) { innerRegisterFixture(name, 'test', fn, registerFixture); }; -export function registerWorkerFixture(name: T, fn: (params: FixtureParameters & WorkerState, runTest: (arg: (WorkerState & FixtureParameters)[T]) => Promise, config: Config) => Promise) { +export function registerWorkerFixture(name: string, fn: (params: any, runTest: (arg: any) => Promise, config: Config) => Promise) { innerRegisterFixture(name, 'worker', fn, registerWorkerFixture); }; -export function registerParameter(name: T, fn: () => WorkerState[T][]) { - registerWorkerFixture(name, async ({}: any, test: (arg0: any) => any) => await test(parameters[name])); +export function registerParameter(name: string, fn: () => any) { + registerWorkerFixture(name, async ({}: any, test: Function) => await test(parameters[name])); parameterRegistrations.set(name, fn); } -function collectRequires(file: string | number, result: Set) { +function collectRequires(file: string, result: Set) { if (result.has(file)) return; result.add(file); @@ -236,8 +233,8 @@ function collectRequires(file: string | number, result: Set) { collectRequires(dep, result); } -export function lookupRegistrations(file: any, scope: any) { - const deps = new Set(); +export function lookupRegistrations(file: string, scope: Scope) { + const deps = new Set(); collectRequires(file, deps); const allDeps = [...deps].reverse(); let result = new Map(); @@ -254,7 +251,7 @@ export function lookupRegistrations(file: any, scope: any) { return result; } -export function rerunRegistrations(file: any, scope: any) { +export function rerunRegistrations(file: string, scope: Scope) { // When we are running several tests in the same worker, we should re-run registrations before // each file. That way we erase potential fixture overrides from the previous test runs. for (const registration of lookupRegistrations(file, scope).values()) diff --git a/test-runner/src/GoldenUtils.js b/test-runner/src/golden.ts similarity index 76% rename from test-runner/src/GoldenUtils.js rename to test-runner/src/golden.ts index 752ccb92d6..fde652d368 100644 --- a/test-runner/src/GoldenUtils.js +++ b/test-runner/src/golden.ts @@ -14,15 +14,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -const path = require('path'); -const fs = require('fs'); -const Diff = require('text-diff'); -const PNG = require('pngjs').PNG; -const jpeg = require('jpeg-js'); -const pixelmatch = require('pixelmatch'); -const c = require('colors/safe'); - -module.exports = {compare}; +import c from 'colors/safe'; +import fs from 'fs'; +import jpeg from 'jpeg-js'; +import path from 'path'; +import pixelmatch from 'pixelmatch'; +import { PNG } from 'pngjs'; +import Diff from 'text-diff'; +import { RunnerConfig } from './runnerConfig'; const extensionToMimeType = { 'png': 'image/png', @@ -37,14 +36,7 @@ const GoldenComparators = { 'text/plain': compareText }; - -/** - * @param {?Object} actualBuffer - * @param {!Buffer} expectedBuffer - * @param {!string} mimeType - * @return {?{diff?: Object, errorMessage?: string}} - */ -function compareImages(actualBuffer, expectedBuffer, mimeType, config = {}) { +function compareImages(actualBuffer: Buffer, expectedBuffer: Buffer, mimeType: string, options = {}): { diff?: object; errorMessage?: string; } | null { if (!actualBuffer || !(actualBuffer instanceof Buffer)) return { errorMessage: 'Actual result should be Buffer.' }; @@ -56,16 +48,11 @@ function compareImages(actualBuffer, expectedBuffer, mimeType, config = {}) { }; } const diff = new PNG({width: expected.width, height: expected.height}); - const count = pixelmatch(expected.data, actual.data, diff.data, expected.width, expected.height, {threshold: 0.2, ...config}); + const count = pixelmatch(expected.data, actual.data, diff.data, expected.width, expected.height, { threshold: 0.2, ...options }); return count > 0 ? { diff: PNG.sync.write(diff) } : null; } -/** - * @param {?Object} actual - * @param {!Buffer} expectedBuffer - * @return {?{diff?: Object, errorMessage?: string, diffExtension?: string}} - */ -function compareText(actual, expectedBuffer) { +function compareText(actual: Buffer, expectedBuffer: Buffer): { diff?: object; errorMessage?: string; diffExtension?: string; } | null { if (typeof actual !== 'string') return { errorMessage: 'Actual result should be string' }; const expected = expectedBuffer.toString('utf-8'); @@ -83,19 +70,14 @@ function compareText(actual, expectedBuffer) { }; } -/** - * @param {?Object} actual - * @param {string} name - * @return {!{pass: boolean, message?: string}} - */ -function compare(actual, name, options) { - const { relativeTestFile, snapshotDir, outputDir, updateSnapshots } = options; - let expectedPath; +export function compare(actual: Buffer, name: string, config: RunnerConfig, testFile: string, options?: { threshold?: number } ): { pass: boolean; message?: string; } { + let expectedPath: string; + const relativeTestFile = path.relative(config.testDir, testFile); const testAssetsDir = relativeTestFile.replace(/\.spec\.[jt]s/, ''); if (path.isAbsolute(name)) expectedPath = name; else - expectedPath = path.join(snapshotDir, testAssetsDir, name); + expectedPath = path.join(config.snapshotDir, testAssetsDir, name); if (!fs.existsSync(expectedPath)) { fs.mkdirSync(path.dirname(expectedPath), { recursive: true }); fs.writeFileSync(expectedPath, actual); @@ -115,11 +97,11 @@ function compare(actual, name, options) { }; } - const result = comparator(actual, expected, mimeType, options.config); + const result = comparator(actual, expected, mimeType, options); if (!result) return { pass: true }; - if (updateSnapshots) { + if (config.updateSnapshots) { fs.mkdirSync(path.dirname(expectedPath), { recursive: true }); fs.writeFileSync(expectedPath, actual); return { @@ -134,7 +116,7 @@ function compare(actual, name, options) { actualPath = addSuffix(expectedPath, '-actual'); diffPath = addSuffix(expectedPath, '-diff', result.diffExtension); } else { - const outputPath = path.join(outputDir, testAssetsDir, name); + const outputPath = path.join(config.outputDir, testAssetsDir, name); fs.mkdirSync(path.dirname(outputPath), { recursive: true }); const expectedPathOut = addSuffix(outputPath, '-expected'); actualPath = addSuffix(outputPath, '-actual'); @@ -162,13 +144,7 @@ function compare(actual, name, options) { }; } -/** - * @param {string} filePath - * @param {string} suffix - * @param {string=} customExtension - * @return {string} - */ -function addSuffix(filePath, suffix, customExtension) { +function addSuffix(filePath: string, suffix: string, customExtension?: string): string { const dirname = path.dirname(filePath); const ext = path.extname(filePath); const name = path.basename(filePath, ext); diff --git a/test-runner/src/index.ts b/test-runner/src/index.ts index 92e4146058..70661b0aaf 100644 --- a/test-runner/src/index.ts +++ b/test-runner/src/index.ts @@ -30,6 +30,17 @@ export { parameters, registerParameter } from './fixtures'; export { RunnerConfig } from './runnerConfig'; export { Suite, Test } from './test'; +declare global { + interface WorkerState { + } + + interface TestState { + } + + interface FixtureParameters { + } +} + let beforeFunctions: Function[] = []; let afterFunctions: Function[] = []; let matrix: Matrix = {}; @@ -39,11 +50,11 @@ global['after'] = (fn: Function) => afterFunctions.push(fn); global['matrix'] = (m: Matrix) => matrix = m; export function registerFixture(name: T, fn: (params: FixtureParameters & WorkerState & TestState, runTest: (arg: TestState[T]) => Promise, config: RunnerConfig, test: Test) => Promise) { - registerFixtureT(name, fn); + registerFixtureT(name, fn); }; export function registerWorkerFixture(name: T, fn: (params: FixtureParameters & WorkerState, runTest: (arg: (WorkerState & FixtureParameters)[T]) => Promise, config: RunnerConfig) => Promise) { - registerWorkerFixtureT(name, fn); + registerWorkerFixtureT(name, fn); }; export function collectTests(config: RunnerConfig, files: string[]): Suite { diff --git a/test-runner/src/runner.ts b/test-runner/src/runner.ts index 18c49a0c0d..8cc0662bec 100644 --- a/test-runner/src/runner.ts +++ b/test-runner/src/runner.ts @@ -19,7 +19,7 @@ import crypto from 'crypto'; import path from 'path'; import { EventEmitter } from 'events'; import { lookupRegistrations, FixturePool } from './fixtures'; -import { Suite } from './test'; +import { Suite, Test, Configuration } from './test'; import { TestRunnerEntry } from './testRunner'; import { RunnerConfig } from './runnerConfig'; @@ -29,8 +29,7 @@ export class Runner extends EventEmitter { private _workerClaimers: (() => void)[] = []; stats: { duration: number; failures: number; passes: number; pending: number; tests: number; }; - private _testById = new Map(); - private _testsByConfiguredFile = new Map(); + private _testById = new Map(); private _queue: TestRunnerEntry[] = []; private _stopCallback: () => void; readonly _config: RunnerConfig; @@ -48,37 +47,34 @@ export class Runner extends EventEmitter { tests: 0, }; - this._testById = new Map(); - this._testsByConfiguredFile = new Map(); this._suite = suite; - this._suite.eachTest(test => { - const configuredFile = `${test.file}::[${test._configurationString}]`; - if (!this._testsByConfiguredFile.has(configuredFile)) { - this._testsByConfiguredFile.set(configuredFile, { - file: test.file, - configuredFile, - ordinals: [], - configurationObject: test._configurationObject, - configurationString: test._configurationString - }); - } - const { ordinals } = this._testsByConfiguredFile.get(configuredFile); - ordinals.push(test._ordinal); - this._testById.set(`${test._ordinal}@${configuredFile}`, test); - }); + for (const suite of this._suite.suites) { + suite.eachTest(test => { + this._testById.set(`${test._ordinal}@${suite.file}::[${suite._configurationString}]`, test); + }); + } if (process.stdout.isTTY) { const total = suite.total(); console.log(); - const jobs = Math.min(config.jobs, this._testsByConfiguredFile.size); + const jobs = Math.min(config.jobs, suite.suites.length); console.log(`Running ${total} test${ total > 1 ? 's' : '' } using ${jobs} worker${ jobs > 1 ? 's' : ''}`); } } - _filesSortedByWorkerHash() { - const result = []; - for (const entry of this._testsByConfiguredFile.values()) - result.push({ ...entry, hash: entry.configurationString + '@' + computeWorkerHash(entry.file) }); + _filesSortedByWorkerHash(): TestRunnerEntry[] { + const result: TestRunnerEntry[] = []; + for (const suite of this._suite.suites) { + const ordinals: number[] = []; + suite.eachTest(test => ordinals.push(test._ordinal) && false); + result.push({ + ordinals, + file: suite.file, + configuration: suite.configuration, + configurationString: suite._configurationString, + hash: suite._configurationString + '@' + computeWorkerHash(suite.file) + }); + } result.sort((a, b) => a.hash < b.hash ? -1 : (a.hash === b.hash ? 0 : 1)); return result; } @@ -258,7 +254,7 @@ class OopWorker extends Worker { await new Promise(f => this.process.once('message', f)); // Ready ack } - run(entry) { + run(entry: TestRunnerEntry) { this.hash = entry.hash; this.process.send({ method: 'run', params: { entry, config: this.runner._config } }); } @@ -316,13 +312,13 @@ class InProcessWorker extends Worker { } } -function chunkFromParams(params) { +function chunkFromParams(params: string | { buffer: string }): string | Buffer { if (typeof params === 'string') return params; return Buffer.from(params.buffer, 'base64'); } -function computeWorkerHash(file) { +function computeWorkerHash(file: string) { // At this point, registrationsByFile contains all the files with worker fixture registrations. // For every test, build the require closure and map each file to fixtures declared in it. // This collection of fixtures is the fingerprint of the worker setup, a "worker hash". @@ -332,5 +328,3 @@ function computeWorkerHash(file) { hash.update(registration.location); return hash.digest('hex'); } - -module.exports = { Runner }; diff --git a/test-runner/src/test.ts b/test-runner/src/test.ts index ce90c957d3..e9ceae95c6 100644 --- a/test-runner/src/test.ts +++ b/test-runner/src/test.ts @@ -29,8 +29,6 @@ export class Test { error: any; _ordinal: number; - _configurationObject: Configuration; - _configurationString: string; _overriddenFn: Function; _startTime: number; @@ -68,6 +66,7 @@ export class Suite { pending = false; file: string; configuration: Configuration; + _configurationString: string; _hooks: { type: string, fn: Function } [] = []; _entries: (Suite | Test)[] = []; diff --git a/test-runner/src/testCollector.ts b/test-runner/src/testCollector.ts index 4f99b33e1e..14775393f2 100644 --- a/test-runner/src/testCollector.ts +++ b/test-runner/src/testCollector.ts @@ -16,7 +16,7 @@ import path from 'path'; import { fixturesForCallback } from './fixtures'; -import { Configuration, Test, Suite } from './test'; +import { Test, Suite } from './test'; import { fixturesUI } from './fixturesUI'; import { RunnerConfig } from './runnerConfig'; @@ -51,7 +51,7 @@ export class TestCollector { return this._hasOnly; } - _addFile(file: string) { + private _addFile(file: string) { const suite = new Suite(''); const revertBabelRequire = fixturesUI(suite, file, this._config.timeout); require(file); @@ -83,35 +83,36 @@ export class TestCollector { if (!generatorConfigurations.length) generatorConfigurations.push([]); - for (const configurationObject of generatorConfigurations) { + for (const configuration of generatorConfigurations) { // Serialize configuration as readable string, we will use it as a hash. const tokens = []; - for (const { name, value } of configurationObject) + for (const { name, value } of configuration) tokens.push(`${name}=${value}`); const configurationString = tokens.join(', '); // Allocate worker for this configuration, add test into it. if (!workerGeneratorConfigurations.has(configurationString)) - workerGeneratorConfigurations.set(configurationString, { configurationObject, configurationString, tests: new Set() }); + workerGeneratorConfigurations.set(configurationString, { configuration, configurationString, tests: new Set() }); workerGeneratorConfigurations.get(configurationString).tests.add(test); } }); // Clone the suite as many times as there are worker hashes. // Only include the tests that requested these generations. - for (const [hash, {configurationObject, configurationString, tests}] of workerGeneratorConfigurations.entries()) { - const clone = this._cloneSuite(suite, configurationObject, configurationString, tests); + for (const [hash, {configuration, configurationString, tests}] of workerGeneratorConfigurations.entries()) { + const clone = this._cloneSuite(suite, tests); this.suite._addSuite(clone); clone.title = path.basename(file) + (hash.length ? `::[${hash}]` : ''); + clone.configuration = configuration; + clone._configurationString = configurationString; } } - _cloneSuite(suite: Suite, configurationObject: Configuration, configurationString: string, tests: Set) { + private _cloneSuite(suite: Suite, tests: Set) { const copy = suite._clone(); copy.only = suite.only; - copy.configuration = configurationObject; for (const entry of suite._entries) { if (entry instanceof Suite) { - copy._addSuite(this._cloneSuite(entry, configurationObject, configurationString, tests)); + copy._addSuite(this._cloneSuite(entry, tests)); } else { const test = entry; if (!tests.has(test)) @@ -121,15 +122,13 @@ export class TestCollector { const testCopy = test._clone(); testCopy.only = test.only; testCopy._ordinal = test._ordinal; - testCopy._configurationObject = configurationObject; - testCopy._configurationString = configurationString; copy._addTest(testCopy); } } return copy; } - _filterOnly(suite) { + private _filterOnly(suite) { const onlySuites = suite.suites.filter(child => this._filterOnly(child) || child.only); const onlyTests = suite.tests.filter(test => test.only); if (onlySuites.length || onlyTests.length) { @@ -140,5 +139,3 @@ export class TestCollector { return false; } } - -module.exports = { TestCollector }; diff --git a/test-runner/src/testRunner.ts b/test-runner/src/testRunner.ts index 9754090425..210b858ea5 100644 --- a/test-runner/src/testRunner.ts +++ b/test-runner/src/testRunner.ts @@ -14,11 +14,10 @@ * limitations under the License. */ -import path from 'path'; import { FixturePool, rerunRegistrations, setParameters } from './fixtures'; import { EventEmitter } from 'events'; import { setCurrentTestFile } from './expect'; -import { Test, Suite } from './test'; +import { Test, Suite, Configuration } from './test'; import { fixturesUI } from './fixturesUI'; import { RunnerConfig } from './runnerConfig'; @@ -27,8 +26,8 @@ export const fixturePool = new FixturePool(); export type TestRunnerEntry = { file: string; ordinals: number[]; - configuredFile: string; - configurationObject: any; + configurationString: string; + configuration: Configuration; hash: string; }; @@ -53,11 +52,11 @@ export class TestRunner extends EventEmitter { this._trialRun = config.trialRun; this._timeout = config.timeout; this._config = config; - this._configuredFile = entry.configuredFile; - for (const {name, value} of entry.configurationObject) + this._configuredFile = entry.file + `::[${entry.configurationString}]`; + for (const {name, value} of entry.configuration) this._parsedGeneratorConfiguration[name] = value; this._parsedGeneratorConfiguration['parallelIndex'] = workerId; - setCurrentTestFile(path.relative(config.testDir, this._file)); + setCurrentTestFile(this._file); } stop() { diff --git a/test-runner/src/transform.js b/test-runner/src/transform.ts similarity index 80% rename from test-runner/src/transform.js rename to test-runner/src/transform.ts index cf0bcdceef..d0304592ee 100644 --- a/test-runner/src/transform.js +++ b/test-runner/src/transform.ts @@ -13,18 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -const crypto = require('crypto'); -const os = require('os'); -const path = require('path'); -const fs = require('fs'); -const pirates = require('pirates'); -const babel = require('@babel/core'); -const sourceMapSupport = require('source-map-support'); + +import * as crypto from 'crypto'; +import * as os from 'os'; +import * as path from 'path'; +import * as fs from 'fs'; +import * as pirates from 'pirates'; +import * as babel from '@babel/core'; +import * as sourceMapSupport from 'source-map-support'; const version = 1; const cacheDir = path.join(os.tmpdir(), 'playwright-transform-cache'); -/** @type {Map} */ -const sourceMaps = new Map(); +const sourceMaps: Map = new Map(); sourceMapSupport.install({ environment: 'node', @@ -42,19 +42,13 @@ sourceMapSupport.install({ } }); -/** - * @param {string} content - * @param {string} filePath - * @return {string} - */ -function calculateCachePath(content, filePath) { +function calculateCachePath(content: string, filePath: string): string { const hash = crypto.createHash('sha1').update(content).update(filePath).update(String(version)).digest('hex'); const fileName = path.basename(filePath, path.extname(filePath)).replace(/\W/g, '') + '_' + hash; - return path.join(cacheDir, hash[0] + hash[1], fileName); } -function installTransform() { +export function installTransform(): () => void { return pirates.addHook((code, filename) => { const cachePath = calculateCachePath(code, filename); const codePath = cachePath + '.js'; @@ -80,5 +74,3 @@ function installTransform() { exts: ['.ts'] }); } - -module.exports = {installTransform};