diff --git a/lib/testcase/visualdiff.d.ts b/lib/testcase/visualdiff.d.ts index 2f298eb..c7b283c 100644 --- a/lib/testcase/visualdiff.d.ts +++ b/lib/testcase/visualdiff.d.ts @@ -1,3 +1,4 @@ +import { Page } from '@playwright/test'; export declare function defineVisualDiffConfig(cases: VisualDiffUrlConfig): VisualDiffTestCases; export declare function defaultTestFunction(testCase: VisualDiff, group: VisualDiffGroup): ({ page, context }: { page: any; @@ -40,6 +41,12 @@ export type VisualDiffGroup = BaseVisualDiff & { pathPrefix?: string; testCases: VisualDiff[]; }; +export interface MockableConstructor { + new (): Mockable; +} +export interface Mockable { + mock(page: Page): Promise; +} /** * An individual test case. */ @@ -51,6 +58,7 @@ export type BaseVisualDiff = { description?: string; representativeUrl?: string; skip?: SkipTest; + mockClass?: MockableConstructor | void; }; /** * A declaration that a test should be skipped. diff --git a/lib/testcase/visualdiff.js b/lib/testcase/visualdiff.js index b303d55..956dc16 100644 --- a/lib/testcase/visualdiff.js +++ b/lib/testcase/visualdiff.js @@ -48,11 +48,18 @@ function defaultTestFunction(testCase, group) { var _this = this; // @ts-ignore return function (_a, testInfo_1) { return __awaiter(_this, [_a, testInfo_1], void 0, function (_b, testInfo) { - var representativeUrl, path; + var mock, representativeUrl, path; var page = _b.page, context = _b.context; return __generator(this, function (_c) { switch (_c.label) { case 0: + if (!(testCase.mockClass != undefined)) return [3 /*break*/, 2]; + mock = new testCase.mockClass; + return [4 /*yield*/, mock.mock(page)]; + case 1: + _c.sent(); + _c.label = 2; + case 2: // Log any errors to the Playwright console too. context.on('weberror', function (webError) { return console.log(webError.error()); }); testInfo.annotations.push({ @@ -77,10 +84,10 @@ function defaultTestFunction(testCase, group) { path = group.pathPrefix + path; } return [4 /*yield*/, page.goto(path)]; - case 1: + case 3: _c.sent(); return [4 /*yield*/, (0, util_1.takeAccessibleScreenshot)(page, testInfo, { fullPage: true })]; - case 2: + case 4: _c.sent(); return [2 /*return*/]; } diff --git a/lib/util/mock/mock.d.ts b/lib/util/mock/mock.d.ts new file mode 100644 index 0000000..8c6cd8c --- /dev/null +++ b/lib/util/mock/mock.d.ts @@ -0,0 +1,5 @@ +import { Page } from '@playwright/test'; +import { Mockable } from "../../testcase"; +export declare class Mock implements Mockable { + mock(page: Page): Promise; +} diff --git a/lib/util/mock/mock.js b/lib/util/mock/mock.js new file mode 100644 index 0000000..6f03caa --- /dev/null +++ b/lib/util/mock/mock.js @@ -0,0 +1,63 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); + return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Mock = void 0; +var youtube_1 = require("./youtube"); +var Mock = /** @class */ (function () { + function Mock() { + } + Mock.prototype.mock = function (page) { + var _this = this; + var f = function (page) { return __awaiter(_this, void 0, void 0, function () { + var youtube; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + youtube = new youtube_1.Youtube(); + return [4 /*yield*/, youtube.mock(page)]; + case 1: + _a.sent(); + return [2 /*return*/]; + } + }); + }); }; + return f(page); + }; + return Mock; +}()); +exports.Mock = Mock; diff --git a/lib/util/mock/youtube.d.ts b/lib/util/mock/youtube.d.ts new file mode 100644 index 0000000..6eb90fa --- /dev/null +++ b/lib/util/mock/youtube.d.ts @@ -0,0 +1,5 @@ +import { Page } from '@playwright/test'; +import { Mockable } from "../../testcase"; +export declare class Youtube implements Mockable { + mock(page: Page): Promise; +} diff --git a/lib/util/mock/youtube.js b/lib/util/mock/youtube.js new file mode 100644 index 0000000..5909bb4 --- /dev/null +++ b/lib/util/mock/youtube.js @@ -0,0 +1,73 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); + return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Youtube = void 0; +var Youtube = /** @class */ (function () { + function Youtube() { + } + Youtube.prototype.mock = function (page) { + return __awaiter(this, void 0, void 0, function () { + var ytEmbedUrl; + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: + ytEmbedUrl = new RegExp('www\.youtube\.com', 'i'); + return [4 /*yield*/, page.route(ytEmbedUrl, function (route) { return __awaiter(_this, void 0, void 0, function () { + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, route.fulfill({ + contentType: 'text/html', + body: "\n\n\n Youtube Video Mock\n \n\n\n
Youtube Video Mock
\n\n\n", + })]; + case 1: + _a.sent(); + return [2 /*return*/]; + } + }); + }); })]; + case 1: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }; + return Youtube; +}()); +exports.Youtube = Youtube; diff --git a/package.json b/package.json index 79ada6c..c111986 100644 --- a/package.json +++ b/package.json @@ -17,5 +17,6 @@ }, "devDependencies": { "typescript": "^5.2.2" - } + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/src/testcase/visualdiff.ts b/src/testcase/visualdiff.ts index a5e99f0..1523c84 100644 --- a/src/testcase/visualdiff.ts +++ b/src/testcase/visualdiff.ts @@ -1,4 +1,4 @@ -import {test, WebError} from '@playwright/test'; +import {Page, test, WebError} from '@playwright/test'; import {takeAccessibleScreenshot} from "../util"; @@ -9,8 +9,12 @@ export function defineVisualDiffConfig(cases: VisualDiffUrlConfig) { export function defaultTestFunction(testCase: VisualDiff, group: VisualDiffGroup) { // @ts-ignore return async ({page, context}, testInfo) => { - // Log any errors to the Playwright console too. + if (testCase.mockClass != undefined) { + const mock = new testCase.mockClass; + await mock.mock(page); + } + // Log any errors to the Playwright console too. context.on('weberror', (webError: WebError) => console.log(webError.error())); testInfo.annotations.push({ type: 'Description', @@ -130,6 +134,13 @@ export type VisualDiffGroup = BaseVisualDiff & { testCases: VisualDiff[], } +export interface MockableConstructor { + new (): Mockable; +} +export interface Mockable { + mock(page: Page): Promise +} + /** * An individual test case. */ @@ -147,6 +158,7 @@ export type BaseVisualDiff = { representativeUrl?: string, // Allow skipping of this test. skip?: SkipTest, + mockClass?: MockableConstructor | void } /** diff --git a/src/util/mock/mock.ts b/src/util/mock/mock.ts new file mode 100644 index 0000000..c96c088 --- /dev/null +++ b/src/util/mock/mock.ts @@ -0,0 +1,14 @@ +import {Page} from '@playwright/test'; +import {Mockable} from "../../testcase"; +import {Youtube} from "./youtube"; + +export class Mock implements Mockable{ + + mock(page: Page): Promise { + const f = async(page: Page): Promise => { + const youtube = new Youtube(); + await youtube.mock(page); + } + return f(page); + } +} \ No newline at end of file diff --git a/src/util/mock/youtube.ts b/src/util/mock/youtube.ts new file mode 100644 index 0000000..d0a19f0 --- /dev/null +++ b/src/util/mock/youtube.ts @@ -0,0 +1,40 @@ +import {Page} from '@playwright/test'; +import {Mockable} from "../../testcase"; + +export class Youtube implements Mockable{ + + public async mock(page: Page): Promise { + const ytEmbedUrl = new RegExp('www\.youtube\.com', 'i'); + await page.route(ytEmbedUrl, async route => { + await route.fulfill({ + contentType: 'text/html', + body: ` + + + Youtube Video Mock + + + +
Youtube Video Mock
+ + +`, + }); + }); + } + +}