Skip to content

Added iframe mocks support #17

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions lib/testcase/visualdiff.d.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -40,6 +41,12 @@ export type VisualDiffGroup = BaseVisualDiff & {
pathPrefix?: string;
testCases: VisualDiff[];
};
export interface MockableConstructor {
new (): Mockable;
}
export interface Mockable {
mock(page: Page): Promise<void>;
}
/**
* An individual test case.
*/
Expand All @@ -51,6 +58,7 @@ export type BaseVisualDiff = {
description?: string;
representativeUrl?: string;
skip?: SkipTest;
mockClass?: MockableConstructor | void;
};
/**
* A declaration that a test should be skipped.
Expand Down
13 changes: 10 additions & 3 deletions lib/testcase/visualdiff.js
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -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*/];
}
Expand Down
5 changes: 5 additions & 0 deletions lib/util/mock/mock.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Page } from '@playwright/test';
import { Mockable } from "../../testcase";
export declare class Mock implements Mockable {
mock(page: Page): Promise<void>;
}
63 changes: 63 additions & 0 deletions lib/util/mock/mock.js
Original file line number Diff line number Diff line change
@@ -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;
5 changes: 5 additions & 0 deletions lib/util/mock/youtube.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Page } from '@playwright/test';
import { Mockable } from "../../testcase";
export declare class Youtube implements Mockable {
mock(page: Page): Promise<void>;
}
73 changes: 73 additions & 0 deletions lib/util/mock/youtube.js
Original file line number Diff line number Diff line change
@@ -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<html>\n<head>\n <title>Youtube Video Mock</title>\n <style>\n body {\n color: white;\n background-color: darkred;\n }\n div {\n position: absolute;\n top: 50%;\n left: 50%;\n margin: 0;\n transform: translate(-50%, -50%);\n font-size: xxx-large;\n text-align: center;\n }\n </style>\n</head>\n<body>\n <div>Youtube Video Mock</div>\n</body>\n</html>\n",
})];
case 1:
_a.sent();
return [2 /*return*/];
}
});
}); })];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
};
return Youtube;
}());
exports.Youtube = Youtube;
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@
},
"devDependencies": {
"typescript": "^5.2.2"
}
},
"packageManager": "[email protected]+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
Comment on lines +20 to +21
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this? We don't have a yarn lockfile or the like.

}
16 changes: 14 additions & 2 deletions src/testcase/visualdiff.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {test, WebError} from '@playwright/test';
import {Page, test, WebError} from '@playwright/test';

import {takeAccessibleScreenshot} from "../util";

Expand All @@ -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',
Expand Down Expand Up @@ -130,6 +134,13 @@ export type VisualDiffGroup = BaseVisualDiff & {
testCases: VisualDiff[],
}

export interface MockableConstructor {
new (): Mockable;
}
export interface Mockable {
mock(page: Page): Promise<void>
}

/**
* An individual test case.
*/
Expand All @@ -147,6 +158,7 @@ export type BaseVisualDiff = {
representativeUrl?: string,
// Allow skipping of this test.
skip?: SkipTest,
mockClass?: MockableConstructor | void
}

/**
Expand Down
14 changes: 14 additions & 0 deletions src/util/mock/mock.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {Page} from '@playwright/test';
import {Mockable} from "../../testcase";
import {Youtube} from "./youtube";

export class Mock implements Mockable{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear to me that loading a Mock here actually does something with YouTube. I'm not clear why we need this extra Mock class. Would it be clearer to have YouTubeMock Implements Mockable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is definitely bad naming. This extra Mock class was using in some projects to call all the mocks mock method. As we only have youtube here for now, makes not a lot of sense. Maybe renaming to EveryIframeServiceMock would make sense.


mock(page: Page): Promise<void> {
const f = async(page: Page): Promise<void> => {
const youtube = new Youtube();
await youtube.mock(page);
}
return f(page);
}
}
40 changes: 40 additions & 0 deletions src/util/mock/youtube.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {Page} from '@playwright/test';
import {Mockable} from "../../testcase";

export class Youtube implements Mockable{

public async mock(page: Page): Promise<void> {
const ytEmbedUrl = new RegExp('www\.youtube\.com', 'i');
await page.route(ytEmbedUrl, async route => {
await route.fulfill({
contentType: 'text/html',
body: `
<html>
<head>
<title>Youtube Video Mock</title>
<style>
body {
color: white;
background-color: darkred;
}
div {
position: absolute;
top: 50%;
left: 50%;
margin: 0;
transform: translate(-50%, -50%);
font-size: xxx-large;
text-align: center;
}
</style>
</head>
<body>
<div>Youtube Video Mock</div>
</body>
</html>
`,
});
});
}

}