Skip to content

Commit

Permalink
chore: implement adapters: test, testCollection, config
Browse files Browse the repository at this point in the history
In order to support gui command for playwright
  • Loading branch information
DudaGod committed Jul 7, 2024
1 parent 0cef1cd commit 70537ce
Show file tree
Hide file tree
Showing 24 changed files with 610 additions and 156 deletions.
15 changes: 15 additions & 0 deletions lib/adapters/config/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {TestAdapter} from '../test';

export interface BrowserConfigAdapter {
readonly id: string;
retry: number;
}

export interface ConfigAdapter {
readonly tolerance: number;
readonly antialiasingTolerance: number;
readonly browserIds: string[];

getBrowserConfig(browserId: string): BrowserConfigAdapter;
getScreenshotPath(test: TestAdapter, stateName: string): string;
}
37 changes: 37 additions & 0 deletions lib/adapters/config/testplane.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type {Config} from 'testplane';
import type {ConfigAdapter} from './';
import {TestplaneTestAdapter} from '../test/testplane';

export class TestplaneConfigAdapter implements ConfigAdapter {
private _config: Config;

static create<T extends TestplaneConfigAdapter>(this: new (config: Config) => T, config: Config): T {
return new this(config);
}

constructor(config: Config) {
this._config = config;
}

get tolerance(): number {
return this._config.tolerance;
}

get antialiasingTolerance(): number {
return this._config.antialiasingTolerance;
}

get browserIds(): string[] {
return this._config.getBrowserIds();
}

getBrowserConfig(browserId: string): ReturnType<Config['forBrowser']> {
return this._config.forBrowser(browserId);
}

getScreenshotPath(test: TestplaneTestAdapter, stateName: string): string {
const {browserId} = test;

return this._config.browsers[browserId].getScreenshotPath(test.original, stateName);
}
}
5 changes: 5 additions & 0 deletions lib/adapters/test-collection/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type {TestAdapter} from '../test';

export interface TestCollectionAdapter {
readonly tests: TestAdapter[];
}
28 changes: 28 additions & 0 deletions lib/adapters/test-collection/testplane.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {TestplaneTestAdapter} from '../test/testplane';
import type {TestCollectionAdapter} from './index';
import type {TestCollection} from 'testplane';

export class TestplaneTestCollectionAdapter implements TestCollectionAdapter {
private _testCollection: TestCollection;
private _testAdapters: TestplaneTestAdapter[];

static create<T>(
this: new (testCollection: TestCollection) => T,
testCollection: TestCollection
): T {
return new this(testCollection);
}

constructor(testCollection: TestCollection) {
this._testCollection = testCollection;
this._testAdapters = this._testCollection.mapTests(test => TestplaneTestAdapter.create(test));
}

get original(): TestCollection {
return this._testCollection;
}

get tests(): TestplaneTestAdapter[] {
return this._testAdapters;
}
}
6 changes: 5 additions & 1 deletion lib/adapters/test-result/playwright.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,11 @@ export const getStatus = (result: PlaywrightTestResult): TestStatus => {
return TestStatus.FAIL;
}

return TestStatus.SKIPPED;
if (result.status === PwtTestStatus.SKIPPED) {
return TestStatus.SKIPPED;
}

return TestStatus.IDLE;
};

const extractErrorMessage = (result: PlaywrightTestResult): string => {
Expand Down
6 changes: 5 additions & 1 deletion lib/adapters/test-result/testplane.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,11 @@ export class TestplaneTestResultAdapter implements ReporterTestResult {
private _attempt: number;
private _status: TestStatus;

static create<T extends TestplaneTestResultAdapter>(this: new (testResult: TestplaneTestResult, options: TestplaneTestResultAdapterOptions) => T, testResult: TestplaneTestResult, options: TestplaneTestResultAdapterOptions): T {
static create(
this: new (testResult: TestplaneTest | TestplaneTestResult, options: TestplaneTestResultAdapterOptions) => TestplaneTestResultAdapter,
testResult: TestplaneTest | TestplaneTestResult,
options: TestplaneTestResultAdapterOptions
): TestplaneTestResultAdapter {
return new this(testResult, options);
}

Expand Down
25 changes: 25 additions & 0 deletions lib/adapters/test/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {TestStatus} from '../../constants';
import type {ReporterTestResult} from '../test-result';
import type {AssertViewResult} from '../../types';

export interface CreateTestResultOpts {
status: TestStatus;
attempt?: number;
assertViewResults?: AssertViewResult[];
error?: Error;
sessionId?: string;
meta?: {
url?: string;
}
}

export interface TestAdapter {
readonly id: string;
readonly pending: boolean;
readonly disabled: boolean;
readonly silentlySkipped: boolean;
readonly browserId: string;
readonly fullName: string;

createTestResult(opts: CreateTestResultOpts): ReporterTestResult;
}
68 changes: 68 additions & 0 deletions lib/adapters/test/testplane.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import {TestplaneTestResultAdapter} from '../test-result/testplane';
import {UNKNOWN_ATTEMPT} from '../../constants';

import type {TestAdapter, CreateTestResultOpts} from './';
import type {Test, Suite} from 'testplane';
import type {ReporterTestResult} from '../test-result';

export class TestplaneTestAdapter implements TestAdapter {
private _test: Test;

static create<T extends TestplaneTestAdapter>(this: new (test: Test) => T, test: Test): T {
return new this(test);
}

constructor(test: Test) {
this._test = test;
}

get original(): Test {
return this._test;
}

get id(): string {
return this._test.id;
}

get pending(): boolean {
return this._test.pending;
}

get disabled(): boolean {
return this._test.disabled;
}

get silentlySkipped(): boolean {
return isSilentlySkipped(this._test);
}

get browserId(): string {
return this._test.browserId;
}

get fullName(): string {
return this._test.fullTitle();
}

createTestResult(opts: CreateTestResultOpts): ReporterTestResult {
const {status, assertViewResults, error, sessionId, meta, attempt = UNKNOWN_ATTEMPT} = opts;
const test = this._test.clone();

[
{key: 'assertViewResults', value: assertViewResults},
{key: 'err', value: error},
{key: 'sessionId', value: sessionId},
{key: 'meta', value: meta}
].forEach(({key, value}) => {
if (value) {
test[key] = value;
}
});

return TestplaneTestResultAdapter.create(test, {attempt, status});
}
}

function isSilentlySkipped(runnable: Test | Suite): boolean {
return Boolean(runnable.silentSkip || runnable.parent && isSilentlySkipped(runnable.parent));
}
11 changes: 7 additions & 4 deletions lib/adapters/tool/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import type {Config, TestCollection} from 'testplane';
import type {CommanderStatic} from '@gemini-testing/commander';

import {TestCollectionAdapter} from '../test-collection';
import {ConfigAdapter} from '../config';
import {GuiApi} from '../../gui/api';
import {EventSource} from '../../gui/event-source';
import {GuiReportBuilder} from '../../report-builder/gui';
import {ToolName} from '../../constants';

import type {ReporterConfig, ImageFile} from '../../types';
import type {TestSpec} from './types';
import type {HtmlReporter} from '../../plugin-api';

export interface ToolAdapterOptionsFromCli {
toolName: ToolName;
Expand All @@ -21,13 +23,14 @@ export interface UpdateReferenceOpts {

export interface ToolAdapter {
readonly toolName: ToolName;
readonly config: Config;
readonly config: ConfigAdapter;
readonly reporterConfig: ReporterConfig;
readonly htmlReporter: HtmlReporter;
readonly guiApi?: GuiApi;

initGuiApi(): void;
readTests(paths: string[], cliTool: CommanderStatic): Promise<TestCollection>;
run(testCollection: TestCollection, tests: TestSpec[], cliTool: CommanderStatic): Promise<boolean>;
readTests(paths: string[], cliTool: CommanderStatic): Promise<TestCollectionAdapter>;
run(testCollection: TestCollectionAdapter, tests: TestSpec[], cliTool: CommanderStatic): Promise<boolean>;

updateReference(opts: UpdateReferenceOpts): void;
handleTestResults(reportBuilder: GuiReportBuilder, eventSource: EventSource): void;
Expand Down
21 changes: 14 additions & 7 deletions lib/adapters/tool/testplane/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import _ from 'lodash';
import Testplane, {type Config, type TestCollection} from 'testplane';
import Testplane, {type Config} from 'testplane';
import type {CommanderStatic} from '@gemini-testing/commander';

import {TestplaneTestCollectionAdapter} from '../../test-collection/testplane';
import {TestplaneConfigAdapter} from '../../config/testplane';
import {GuiApi} from '../../../gui/api';
import {parseConfig} from '../../../config';
import {HtmlReporter} from '../../../plugin-api';
Expand All @@ -15,6 +17,7 @@ import {ToolName} from '../../../constants';
import type {ToolAdapter, ToolAdapterOptionsFromCli, UpdateReferenceOpts} from '../index';
import type {TestSpec, CustomGuiActionPayload} from '../types';
import type {ReporterConfig, CustomGuiItem} from '../../../types';
import type {ConfigAdapter} from '../../config/index';

type HtmlReporterApi = {
gui: ApiFacade;
Expand All @@ -39,6 +42,7 @@ type Options = ToolAdapterOptionsFromCli | OptionsFromPlugin;
export class TestplaneToolAdapter implements ToolAdapter {
private _toolName: ToolName;
private _tool: TestplaneWithHtmlReporter;
private _config: ConfigAdapter;
private _reporterConfig: ReporterConfig;
private _htmlReporter: HtmlReporter;
private _guiApi?: GuiApi;
Expand All @@ -64,6 +68,7 @@ export class TestplaneToolAdapter implements ToolAdapter {
}

this._toolName = opts.toolName;
this._config = TestplaneConfigAdapter.create(this._tool.config);
this._htmlReporter = HtmlReporter.create(this._reporterConfig, {toolName: ToolName.Testplane});

// in order to be able to use it from other plugins as an API
Expand All @@ -74,8 +79,8 @@ export class TestplaneToolAdapter implements ToolAdapter {
return this._toolName;
}

get config(): Config {
return this._tool.config;
get config(): ConfigAdapter {
return this._config;
}

get reporterConfig(): ReporterConfig {
Expand All @@ -97,17 +102,19 @@ export class TestplaneToolAdapter implements ToolAdapter {
this._tool.gui = this._guiApi.gui;
}

async readTests(paths: string[], cliTool: CommanderStatic): Promise<TestCollection> {
async readTests(paths: string[], cliTool: CommanderStatic): Promise<TestplaneTestCollectionAdapter> {
const {grep, set: sets, browser: browsers} = cliTool;
const replMode = getReplModeOption(cliTool);

return this._tool.readTests(paths, {grep, sets, browsers, replMode});
const testCollection = await this._tool.readTests(paths, {grep, sets, browsers, replMode});

return TestplaneTestCollectionAdapter.create(testCollection);
}

async run(testCollection: TestCollection, tests: TestSpec[] = [], cliTool: CommanderStatic): Promise<boolean> {
async run(testCollectionAdapter: TestplaneTestCollectionAdapter, tests: TestSpec[] = [], cliTool: CommanderStatic): Promise<boolean> {
const {grep, set: sets, browser: browsers, devtools = false} = cliTool;
const replMode = getReplModeOption(cliTool);
const runner = createTestRunner(testCollection, tests);
const runner = createTestRunner(testCollectionAdapter.original, tests);

return runner.run((collection) => this._tool.run(collection, {grep, sets, browsers, devtools, replMode}));
}
Expand Down
2 changes: 1 addition & 1 deletion lib/adapters/tool/testplane/runner/all-test-runner.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type {TestCollection} from 'testplane';
import {BaseRunner} from './runner';
import type {TestCollection} from 'testplane';

export class AllTestRunner extends BaseRunner {
override run<U>(runHandler: (testCollection: TestCollection) => U): U {
Expand Down
2 changes: 1 addition & 1 deletion lib/adapters/tool/testplane/runner/specific-test-runner.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type {TestCollection} from 'testplane';
import {BaseRunner} from './runner';
import type {TestSpec} from '../../types';
import type {TestCollection} from 'testplane';

export class SpecificTestRunner extends BaseRunner {
private _tests: TestSpec[];
Expand Down
13 changes: 6 additions & 7 deletions lib/cli/commands/remove-unused-screens/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,14 @@ exports.getTestsFromFs = async (toolAdapter) => {

const testCollection = await toolAdapter.readTests([], {silent: true});

testCollection.eachTest((test, browserId) => {
const fullTitle = test.fullTitle();
const id = `${fullTitle} ${browserId}`;
const screenPattern = toolAdapter.config.browsers[browserId].getScreenshotPath(test, '*');
for (const test of testCollection.tests) {
const id = `${test.fullName} ${test.browserId}`;
const screenPattern = toolAdapter.config.getScreenshotPath(test, '*');

tests.byId[id] = {screenPattern, ...test};
tests.browserIds.add(browserId);
uniqTests.add(fullTitle);
});
tests.browserIds.add(test.browserId);
uniqTests.add(test.fullName);
}

tests.count = uniqTests.size;

Expand Down
8 changes: 3 additions & 5 deletions lib/gui/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,13 @@ import _ from 'lodash';
import {ToolRunner, ToolRunnerTree, UndoAcceptImagesResult} from './tool-runner';
import {TestBranch, TestEqualDiffsData, TestRefUpdateData} from '../tests-tree-builder/gui';

import type {Config} from 'testplane';
import type {ServerArgs} from './index';
import type {TestSpec} from '../adapters/tool/types';

type BrowserConfig = ReturnType<Config['forBrowser']>;
import type {BrowserConfigAdapter} from '../adapters/config/index';

export class App {
private _toolRunner: ToolRunner;
private _browserConfigs: BrowserConfig[];
private _browserConfigs: BrowserConfigAdapter[];
private _retryCache: Record<string, number>;

static create<T extends App>(this: new (args: ServerArgs) => T, args: ServerArgs): T {
Expand Down Expand Up @@ -46,7 +44,7 @@ export class App {

private async _runWithoutRetries(tests: TestSpec[]): Promise<boolean> {
if (_.isEmpty(this._browserConfigs)) {
this._browserConfigs = _.map(this._toolRunner.config.getBrowserIds(), (id) => this._toolRunner.config.forBrowser(id));
this._browserConfigs = _.map(this._toolRunner.config.browserIds, (id) => this._toolRunner.config.getBrowserConfig(id));
}

this._disableRetries();
Expand Down
Loading

0 comments on commit 70537ce

Please sign in to comment.