@stencil/playwright
Version:
Testing adapter to use Playwright with Stencil
953 lines (924 loc) • 31.5 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// node_modules/deepmerge/dist/cjs.js
var require_cjs = __commonJS({
"node_modules/deepmerge/dist/cjs.js"(exports2, module2) {
"use strict";
var isMergeableObject = function isMergeableObject2(value) {
return isNonNullObject(value) && !isSpecial(value);
};
function isNonNullObject(value) {
return !!value && typeof value === "object";
}
function isSpecial(value) {
var stringValue = Object.prototype.toString.call(value);
return stringValue === "[object RegExp]" || stringValue === "[object Date]" || isReactElement(value);
}
var canUseSymbol = typeof Symbol === "function" && Symbol.for;
var REACT_ELEMENT_TYPE = canUseSymbol ? Symbol.for("react.element") : 60103;
function isReactElement(value) {
return value.$$typeof === REACT_ELEMENT_TYPE;
}
function emptyTarget(val) {
return Array.isArray(val) ? [] : {};
}
function cloneUnlessOtherwiseSpecified(value, options) {
return options.clone !== false && options.isMergeableObject(value) ? deepmerge(emptyTarget(value), value, options) : value;
}
function defaultArrayMerge(target, source, options) {
return target.concat(source).map(function(element) {
return cloneUnlessOtherwiseSpecified(element, options);
});
}
function getMergeFunction(key, options) {
if (!options.customMerge) {
return deepmerge;
}
var customMerge = options.customMerge(key);
return typeof customMerge === "function" ? customMerge : deepmerge;
}
function getEnumerableOwnPropertySymbols(target) {
return Object.getOwnPropertySymbols ? Object.getOwnPropertySymbols(target).filter(function(symbol) {
return Object.propertyIsEnumerable.call(target, symbol);
}) : [];
}
function getKeys(target) {
return Object.keys(target).concat(getEnumerableOwnPropertySymbols(target));
}
function propertyIsOnObject(object, property) {
try {
return property in object;
} catch (_) {
return false;
}
}
function propertyIsUnsafe(target, key) {
return propertyIsOnObject(target, key) && !(Object.hasOwnProperty.call(target, key) && Object.propertyIsEnumerable.call(target, key));
}
function mergeObject(target, source, options) {
var destination = {};
if (options.isMergeableObject(target)) {
getKeys(target).forEach(function(key) {
destination[key] = cloneUnlessOtherwiseSpecified(target[key], options);
});
}
getKeys(source).forEach(function(key) {
if (propertyIsUnsafe(target, key)) {
return;
}
if (propertyIsOnObject(target, key) && options.isMergeableObject(source[key])) {
destination[key] = getMergeFunction(key, options)(target[key], source[key], options);
} else {
destination[key] = cloneUnlessOtherwiseSpecified(source[key], options);
}
});
return destination;
}
function deepmerge(target, source, options) {
options = options || {};
options.arrayMerge = options.arrayMerge || defaultArrayMerge;
options.isMergeableObject = options.isMergeableObject || isMergeableObject;
options.cloneUnlessOtherwiseSpecified = cloneUnlessOtherwiseSpecified;
var sourceIsArray = Array.isArray(source);
var targetIsArray = Array.isArray(target);
var sourceAndTargetTypesMatch = sourceIsArray === targetIsArray;
if (!sourceAndTargetTypesMatch) {
return cloneUnlessOtherwiseSpecified(source, options);
} else if (sourceIsArray) {
return options.arrayMerge(target, source, options);
} else {
return mergeObject(target, source, options);
}
}
deepmerge.all = function deepmergeAll(array, options) {
if (!Array.isArray(array)) {
throw new Error("first argument should be an array");
}
return array.reduce(function(prev, next) {
return deepmerge(prev, next, options);
}, {});
};
var deepmerge_1 = deepmerge;
module2.exports = deepmerge_1;
}
});
// node_modules/fast-deep-equal/index.js
var require_fast_deep_equal = __commonJS({
"node_modules/fast-deep-equal/index.js"(exports2, module2) {
"use strict";
module2.exports = function equal(a, b) {
if (a === b)
return true;
if (a && b && typeof a == "object" && typeof b == "object") {
if (a.constructor !== b.constructor)
return false;
var length, i, keys;
if (Array.isArray(a)) {
length = a.length;
if (length != b.length)
return false;
for (i = length; i-- !== 0; )
if (!equal(a[i], b[i]))
return false;
return true;
}
if (a.constructor === RegExp)
return a.source === b.source && a.flags === b.flags;
if (a.valueOf !== Object.prototype.valueOf)
return a.valueOf() === b.valueOf();
if (a.toString !== Object.prototype.toString)
return a.toString() === b.toString();
keys = Object.keys(a);
length = keys.length;
if (length !== Object.keys(b).length)
return false;
for (i = length; i-- !== 0; )
if (!Object.prototype.hasOwnProperty.call(b, keys[i]))
return false;
for (i = length; i-- !== 0; ) {
var key = keys[i];
if (!equal(a[key], b[key]))
return false;
}
return true;
}
return a !== a && b !== b;
};
}
});
// src/index.ts
var src_exports = {};
__export(src_exports, {
createConfig: () => createConfig,
goto: () => goto,
locator: () => locator,
matchers: () => matchers,
setContent: () => setContent,
spyOnEvent: () => spyOnEvent,
test: () => test,
waitForChanges: () => waitForChanges
});
module.exports = __toCommonJS(src_exports);
// src/create-config.ts
var import_deepmerge = __toESM(require_cjs());
// src/load-config-meta.ts
var import_compiler = require("@stencil/core/compiler");
// node_modules/find-up/index.js
var import_node_path2 = __toESM(require("node:path"), 1);
// node_modules/locate-path/index.js
var import_node_process = __toESM(require("node:process"), 1);
var import_node_path = __toESM(require("node:path"), 1);
var import_node_fs = __toESM(require("node:fs"), 1);
var import_node_url = require("node:url");
// node_modules/p-locate/node_modules/yocto-queue/index.js
var Node = class {
value;
next;
constructor(value) {
this.value = value;
}
};
var Queue = class {
#head;
#tail;
#size;
constructor() {
this.clear();
}
enqueue(value) {
const node = new Node(value);
if (this.#head) {
this.#tail.next = node;
this.#tail = node;
} else {
this.#head = node;
this.#tail = node;
}
this.#size++;
}
dequeue() {
const current = this.#head;
if (!current) {
return;
}
this.#head = this.#head.next;
this.#size--;
return current.value;
}
clear() {
this.#head = void 0;
this.#tail = void 0;
this.#size = 0;
}
get size() {
return this.#size;
}
*[Symbol.iterator]() {
let current = this.#head;
while (current) {
yield current.value;
current = current.next;
}
}
};
// node_modules/p-locate/node_modules/p-limit/index.js
function pLimit(concurrency) {
if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) {
throw new TypeError("Expected `concurrency` to be a number from 1 and up");
}
const queue = new Queue();
let activeCount = 0;
const next = () => {
activeCount--;
if (queue.size > 0) {
queue.dequeue()();
}
};
const run = async (fn, resolve, args) => {
activeCount++;
const result = (async () => fn(...args))();
resolve(result);
try {
await result;
} catch {
}
next();
};
const enqueue = (fn, resolve, args) => {
queue.enqueue(run.bind(void 0, fn, resolve, args));
(async () => {
await Promise.resolve();
if (activeCount < concurrency && queue.size > 0) {
queue.dequeue()();
}
})();
};
const generator = (fn, ...args) => new Promise((resolve) => {
enqueue(fn, resolve, args);
});
Object.defineProperties(generator, {
activeCount: {
get: () => activeCount
},
pendingCount: {
get: () => queue.size
},
clearQueue: {
value: () => {
queue.clear();
}
}
});
return generator;
}
// node_modules/p-locate/index.js
var EndError = class extends Error {
constructor(value) {
super();
this.value = value;
}
};
var testElement = async (element, tester) => tester(await element);
var finder = async (element) => {
const values = await Promise.all(element);
if (values[1] === true) {
throw new EndError(values[0]);
}
return false;
};
async function pLocate(iterable, tester, {
concurrency = Number.POSITIVE_INFINITY,
preserveOrder = true
} = {}) {
const limit = pLimit(concurrency);
const items = [...iterable].map((element) => [element, limit(testElement, element, tester)]);
const checkLimit = pLimit(preserveOrder ? 1 : Number.POSITIVE_INFINITY);
try {
await Promise.all(items.map((element) => checkLimit(finder, element)));
} catch (error) {
if (error instanceof EndError) {
return error.value;
}
throw error;
}
}
// node_modules/locate-path/index.js
var typeMappings = {
directory: "isDirectory",
file: "isFile"
};
function checkType(type) {
if (Object.hasOwnProperty.call(typeMappings, type)) {
return;
}
throw new Error(`Invalid type specified: ${type}`);
}
var matchType = (type, stat) => stat[typeMappings[type]]();
var toPath = (urlOrPath) => urlOrPath instanceof URL ? (0, import_node_url.fileURLToPath)(urlOrPath) : urlOrPath;
async function locatePath(paths, {
cwd = import_node_process.default.cwd(),
type = "file",
allowSymlinks = true,
concurrency,
preserveOrder
} = {}) {
checkType(type);
cwd = toPath(cwd);
const statFunction = allowSymlinks ? import_node_fs.promises.stat : import_node_fs.promises.lstat;
return pLocate(paths, async (path_) => {
try {
const stat = await statFunction(import_node_path.default.resolve(cwd, path_));
return matchType(type, stat);
} catch {
return false;
}
}, { concurrency, preserveOrder });
}
// node_modules/unicorn-magic/node.js
var import_node_url2 = require("node:url");
function toPath2(urlOrPath) {
return urlOrPath instanceof URL ? (0, import_node_url2.fileURLToPath)(urlOrPath) : urlOrPath;
}
// node_modules/find-up/index.js
var findUpStop = Symbol("findUpStop");
async function findUpMultiple(name, options = {}) {
let directory = import_node_path2.default.resolve(toPath2(options.cwd) ?? "");
const { root } = import_node_path2.default.parse(directory);
const stopAt = import_node_path2.default.resolve(directory, toPath2(options.stopAt ?? root));
const limit = options.limit ?? Number.POSITIVE_INFINITY;
const paths = [name].flat();
const runMatcher = async (locateOptions) => {
if (typeof name !== "function") {
return locatePath(paths, locateOptions);
}
const foundPath = await name(locateOptions.cwd);
if (typeof foundPath === "string") {
return locatePath([foundPath], locateOptions);
}
return foundPath;
};
const matches = [];
while (true) {
const foundPath = await runMatcher({ ...options, cwd: directory });
if (foundPath === findUpStop) {
break;
}
if (foundPath) {
matches.push(import_node_path2.default.resolve(directory, foundPath));
}
if (directory === stopAt || matches.length >= limit) {
break;
}
directory = import_node_path2.default.dirname(directory);
}
return matches;
}
async function findUp(name, options = {}) {
const matches = await findUpMultiple(name, { ...options, limit: 1 });
return matches[0];
}
// src/load-config-meta.ts
var import_fs = require("fs");
var import_path = require("path");
var DEFAULT_NAMESPACE = "app";
var DEFAULT_BASE_URL = "http://localhost:3333";
var DEFAULT_WEB_SERVER_URL = `${DEFAULT_BASE_URL}/ping`;
var DEFAULT_STENCIL_ENTRY_PATH_PREFIX = "./build";
var DEFAULT_STENCIL_ENTRY_PATH = `${DEFAULT_STENCIL_ENTRY_PATH_PREFIX}/${DEFAULT_NAMESPACE}`;
var loadConfigMeta = async () => {
let baseURL = DEFAULT_BASE_URL;
let webServerUrl = DEFAULT_WEB_SERVER_URL;
let stencilNamespace = DEFAULT_NAMESPACE;
let stencilEntryPath = DEFAULT_STENCIL_ENTRY_PATH;
const stencilConfigPath = await findUp(["stencil.config.ts", "stencil.config.js"]);
if (stencilConfigPath && (0, import_fs.existsSync)(stencilConfigPath)) {
const { devServer, fsNamespace, outputTargets } = (await (0, import_compiler.loadConfig)({ configPath: stencilConfigPath })).config;
const wwwTarget = outputTargets.find((o) => o.type === "www");
if (wwwTarget) {
let relativePath = (0, import_path.relative)(devServer.root, wwwTarget.dir);
relativePath = relativePath === "" ? "." : relativePath;
if (!relativePath.startsWith(".")) {
relativePath = `./${relativePath}`;
}
stencilEntryPath = `${relativePath}/build/${fsNamespace}`;
} else {
stencilEntryPath = `${DEFAULT_STENCIL_ENTRY_PATH_PREFIX}/${fsNamespace}`;
console.warn(
`No "www" output target found in the Stencil config. Using default entry path: "${stencilEntryPath}". Tests using 'setContent' may fail to execute.`
);
}
baseURL = `${devServer.protocol}://${devServer.address}:${devServer.port}`;
webServerUrl = `${baseURL}${devServer.pingRoute ?? ""}`;
stencilNamespace = fsNamespace;
} else {
const msg = stencilConfigPath ? `Unable to find your project's Stencil configuration file, starting from '${stencilConfigPath}'. Falling back to defaults.` : `No Stencil config file was found matching the glob 'stencil.config.{ts,js}' in the current or parent directories. Falling back to defaults.`;
console.warn(msg);
}
return {
baseURL,
webServerUrl,
stencilNamespace,
stencilEntryPath
};
};
// src/create-config.ts
var createConfig = async (overrides = {}) => {
const { webServerUrl, baseURL, stencilEntryPath, stencilNamespace } = await loadConfigMeta();
process.env["STENCIL_NAMESPACE" /* STENCIL_NAMESPACE */] = stencilNamespace;
process.env["STENCIL_ENTRY_PATH" /* STENCIL_ENTRY_PATH */] = stencilEntryPath;
return (0, import_deepmerge.default)(
{
testMatch: "*.e2e.ts",
use: {
baseURL
},
webServer: {
command: "stencil build --dev --watch --serve --no-open",
url: webServerUrl,
reuseExistingServer: !!!process.env.CI,
// Max time to wait for dev server to start before aborting, defaults to 60000 (60 seconds)
timeout: void 0,
// Pipe the dev server output to the console
// Gives visibility to the developer if the dev server fails to start
stdout: "pipe"
}
},
overrides
);
};
// src/matchers/to-have-first-received-event-detail.ts
var import_test = require("@playwright/test");
var import_fast_deep_equal = __toESM(require_fast_deep_equal());
function toHaveFirstReceivedEventDetail(eventSpy, eventDetail) {
if (eventSpy === null || eventSpy === void 0) {
return {
message: () => `expected spy to have received event, but it was not defined`,
pass: false
};
}
if (typeof eventSpy.then === "function") {
return {
message: () => `expected spy to have received event, but it was not resolved (did you forget an await operator?).`,
pass: false
};
}
if (eventSpy.eventName === null || eventSpy.eventName === void 0) {
return {
message: () => `toHaveReceivedEventDetail did not receive an event spy`,
pass: false
};
}
if (eventSpy.firstEvent === null || eventSpy.firstEvent === void 0) {
return {
message: () => `event "${eventSpy.eventName}" was not received`,
pass: false
};
}
const pass = (0, import_fast_deep_equal.default)(eventSpy.firstEvent.detail, eventDetail);
(0, import_test.expect)(eventSpy.lastEvent.detail).toEqual(eventDetail);
return {
message: () => `expected event "${eventSpy.eventName}" detail to ${pass ? "not " : ""}equal`,
pass
};
}
// src/matchers/to-have-nth-received-event-detail.ts
var import_test2 = require("@playwright/test");
var import_fast_deep_equal2 = __toESM(require_fast_deep_equal());
function toHaveNthReceivedEventDetail(eventSpy, index, eventDetail) {
if (eventSpy === null || eventSpy === void 0) {
return {
message: () => `expected spy to have received event, but it was not defined`,
pass: false
};
}
if (typeof eventSpy.then === "function") {
return {
message: () => `expected spy to have received event, but it was not resolved (did you forget an await operator?).`,
pass: false
};
}
if (eventSpy.eventName === null || eventSpy.eventName === void 0) {
return {
message: () => `toHaveReceivedEventDetail did not receive an event spy`,
pass: false
};
}
if (eventSpy.firstEvent === null || eventSpy.firstEvent === void 0) {
return {
message: () => `event "${eventSpy.eventName}" was not received`,
pass: false
};
}
const event = eventSpy.events[index];
if (event === null || event === void 0) {
return {
message: () => `event at index ${index} was not received`,
pass: false
};
}
const pass = (0, import_fast_deep_equal2.default)(event.detail, eventDetail);
(0, import_test2.expect)(event.detail).toEqual(eventDetail);
return {
message: () => `expected event "${eventSpy.eventName}" detail to ${pass ? "not " : ""}equal`,
pass
};
}
// src/matchers/to-have-received-event.ts
function toHaveReceivedEvent(eventSpy) {
if (eventSpy === void 0 || eventSpy === null) {
return {
message: () => `expected spy to have received event, but it was not defined`,
pass: false
};
}
if (typeof eventSpy.then === "function") {
return {
message: () => `expected spy to have received event, but it was not resolved (did you forget an await operator?).`,
pass: false
};
}
const pass = eventSpy.events.length > 0;
if (pass) {
return {
message: () => `expected to have called ${eventSpy.eventName} event`,
pass: true
};
}
return {
message: () => `expected to have not called ${eventSpy.eventName} event`,
pass: false
};
}
// src/matchers/to-have-received-event-detail.ts
var import_test3 = require("@playwright/test");
var import_fast_deep_equal3 = __toESM(require_fast_deep_equal());
function toHaveReceivedEventDetail(eventSpy, eventDetail) {
if (eventSpy === null || eventSpy === void 0) {
return {
message: () => `toHaveReceivedEventDetail event spy is null`,
pass: false
};
}
if (typeof eventSpy.then === "function") {
return {
message: () => `expected spy to have received event, but it was not resolved (did you forget an await operator?).`,
pass: false
};
}
if (!eventSpy.eventName) {
return {
message: () => `toHaveReceivedEventDetail did not receive an event spy`,
pass: false
};
}
if (eventSpy.lastEvent === null || eventSpy.lastEvent === void 0) {
return {
message: () => `event "${eventSpy.eventName}" was not received`,
pass: false
};
}
const pass = (0, import_fast_deep_equal3.default)(eventSpy.lastEvent.detail, eventDetail);
(0, import_test3.expect)(eventSpy.lastEvent.detail).toEqual(eventDetail);
return {
message: () => `expected event "${eventSpy.eventName}" detail to ${pass ? "not " : ""}equal`,
pass
};
}
// src/matchers/to-have-received-event-times.ts
function toHaveReceivedEventTimes(eventSpy, count) {
if (!eventSpy) {
return {
message: () => `toHaveReceivedEventTimes event spy is null`,
pass: false
};
}
if (typeof eventSpy.then === "function") {
return {
message: () => `expected spy to have received event, but it was not resolved (did you forget an await operator?).`,
pass: false
};
}
if (!eventSpy.eventName) {
return {
message: () => `toHaveReceivedEventTimes did not receive an event spy`,
pass: false
};
}
const pass = eventSpy.length === count;
return {
message: () => `expected event "${eventSpy.eventName}" to have been called ${count} times, but it was called ${eventSpy.events.length} times`,
pass
};
}
// src/matchers/index.ts
var matchers = {
toHaveReceivedEvent,
toHaveReceivedEventDetail,
toHaveReceivedEventTimes,
toHaveFirstReceivedEventDetail,
toHaveNthReceivedEventDetail
};
// src/page/utils/goto.ts
var goto = async (page, url, originalFn, options) => {
const result = await Promise.all([
page.waitForFunction(() => window.testAppLoaded === true, {
// This timeout was taken from the existing Playwright adapter in the Ionic Framework repository.
// They tested this number and found it to be a reliable timeout for the Stencil components to be hydrated.
timeout: 4750
}),
originalFn(url, options)
]);
return result[1];
};
// src/page/event-spy.ts
var EventSpy = class {
constructor(eventName) {
this.eventName = eventName;
/**
* Keeping track of a cursor ensures that no two spy.next() calls point to the same event.
*/
this.cursor = 0;
this.queuedHandler = [];
this.events = [];
}
get length() {
return this.events.length;
}
get firstEvent() {
return this.events[0] ?? null;
}
get lastEvent() {
return this.events[this.events.length - 1] ?? null;
}
next() {
const { cursor } = this;
this.cursor++;
const next = this.events[cursor];
if (next !== void 0) {
return Promise.resolve(next);
} else {
let resolve;
const promise = new Promise((r) => resolve = r);
this.queuedHandler.push(resolve);
return promise.then(() => this.events[cursor]);
}
}
push(ev) {
this.events.push(ev);
const next = this.queuedHandler.shift();
if (next) {
next();
}
}
};
var initPageEvents = async (page) => {
page._e2eEventsIds = 0;
page._e2eEvents = /* @__PURE__ */ new Map();
await page.exposeFunction("stencilOnEvent", (id, ev) => {
const context = page._e2eEvents.get(id);
if (context) {
context.callback(ev);
}
});
};
var addE2EListener = async (page, elmHandle, eventName, callback) => {
const id = page._e2eEventsIds++;
page._e2eEvents.set(id, {
eventName,
callback
});
await elmHandle.evaluate(
(elm, [eventName2, id2]) => {
window.stencilSerializeEventTarget = (target) => {
if (!target) {
return null;
}
if (target === window) {
return { serializedWindow: true };
}
if (target === document) {
return { serializedDocument: true };
}
if (target.nodeType != null) {
const serializedElement = {
serializedElement: true,
nodeName: target.nodeName,
nodeValue: target.nodeValue,
nodeType: target.nodeType,
tagName: target.tagName,
className: target.className,
id: target.id
};
return serializedElement;
}
return null;
};
window.serializeStencilEvent = (orgEv) => {
const serializedEvent = {
bubbles: orgEv.bubbles,
cancelBubble: orgEv.cancelBubble,
cancelable: orgEv.cancelable,
composed: orgEv.composed,
currentTarget: window.stencilSerializeEventTarget(orgEv.currentTarget),
defaultPrevented: orgEv.defaultPrevented,
detail: orgEv.detail,
eventPhase: orgEv.eventPhase,
isTrusted: orgEv.isTrusted,
returnValue: orgEv.returnValue,
srcElement: window.stencilSerializeEventTarget(orgEv.srcElement),
target: window.stencilSerializeEventTarget(orgEv.target),
timeStamp: orgEv.timeStamp,
type: orgEv.type,
isSerializedEvent: true
};
return serializedEvent;
};
elm.addEventListener(eventName2, (ev) => {
window.stencilOnEvent(id2, window.serializeStencilEvent(ev));
});
},
[eventName, id]
);
};
// src/page/utils/locator.ts
var locator = (page, originalFn, selector, options) => {
const locator2 = originalFn(selector, options);
locator2.spyOnEvent = async (eventName) => {
const spy = new EventSpy(eventName);
const handle = await locator2.evaluateHandle((node) => node);
await addE2EListener(page, handle, eventName, (ev) => spy.push(ev));
return spy;
};
return locator2;
};
// src/page/utils/set-content.ts
var setContent = async (page, html, testInfo, options) => {
if (page.isClosed()) {
throw new Error("setContent unavailable: page is already closed");
}
const baseUrl = testInfo.project.use.baseURL;
const baseEntryPath = process.env.STENCIL_ENTRY_PATH;
const output = `
<!DOCTYPE html>
<html lang="en">
<head>
<title>Stencil Playwright Test</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
<script src="${baseEntryPath}.js" nomodule></script>
<script type="module" src="${baseEntryPath}.esm.js"></script>
</head>
<body>
${html}
</body>
</html>
`;
if (baseUrl) {
await page.route(baseUrl, (route) => {
if (route.request().url() === `${baseUrl}/`) {
route.fulfill({
status: 200,
contentType: "text/html",
body: output
});
} else {
route.continue();
}
});
await page.goto(`${baseUrl}#`, options);
} else {
throw new Error("setContent unavailable: no dev server base URL provided");
}
};
// src/page/utils/spy-on-event.ts
var spyOnEvent = async (page, eventName) => {
const spy = new EventSpy(eventName);
const handle = await page.evaluateHandle(() => window);
await addE2EListener(page, handle, eventName, (ev) => spy.push(ev));
return spy;
};
// src/page/utils/wait-for-changes.ts
var waitForChanges = async (page, timeoutMs = 100) => {
try {
if (page.isClosed()) {
return;
}
await page.evaluate(() => {
return new Promise((resolve) => {
requestAnimationFrame(() => {
const promiseChain = [];
const waitComponentOnReady = (elm, promises) => {
if ("shadowRoot" in elm && elm.shadowRoot instanceof ShadowRoot) {
waitComponentOnReady(elm.shadowRoot, promises);
}
const children = elm.children;
const len = children.length;
for (let i = 0; i < len; i++) {
const childElm = children[i];
const childStencilElm = childElm;
if (childElm.tagName.includes("-") && typeof childStencilElm.componentOnReady === "function") {
promises.push(childStencilElm.componentOnReady());
}
waitComponentOnReady(childElm, promises);
}
};
waitComponentOnReady(document.documentElement, promiseChain);
Promise.all(promiseChain).then(() => resolve()).catch(() => resolve());
});
});
});
if (page.isClosed()) {
return;
}
await page.waitForTimeout(timeoutMs);
} catch (e) {
console.error(e);
}
};
// src/playwright-page.ts
var import_test4 = require("@playwright/test");
async function extendPageFixture(page) {
if (!process.env["STENCIL_NAMESPACE" /* STENCIL_NAMESPACE */] || !process.env["STENCIL_ENTRY_PATH" /* STENCIL_ENTRY_PATH */]) {
const { stencilNamespace, stencilEntryPath } = await loadConfigMeta();
if (!process.env["STENCIL_NAMESPACE" /* STENCIL_NAMESPACE */]) {
process.env["STENCIL_NAMESPACE" /* STENCIL_NAMESPACE */] = stencilNamespace;
}
if (!process.env["STENCIL_ENTRY_PATH" /* STENCIL_ENTRY_PATH */]) {
process.env["STENCIL_ENTRY_PATH" /* STENCIL_ENTRY_PATH */] = stencilEntryPath;
}
}
const originalGoto = page.goto.bind(page);
const originalLocator = page.locator.bind(page);
await page.addInitScript(() => {
window.addEventListener("appload", () => {
window.testAppLoaded = true;
});
});
page.goto = (url, options) => goto(page, url, originalGoto, options);
page.setContent = (html, options) => setContent(page, html, test.info(), options);
page.locator = (selector, options) => locator(page, originalLocator, selector, options);
page.waitForChanges = (timeoutMs) => waitForChanges(page, timeoutMs);
page.spyOnEvent = (eventName) => spyOnEvent(page, eventName);
await initPageEvents(page);
return page;
}
var test = import_test4.test.extend({
page: async ({ page }, use) => {
page = await extendPageFixture(page);
await use(page);
},
skip: {
browser: (browserNameOrFunction, reason = `The functionality that is being tested is not applicable to this browser.`) => {
const browserName = import_test4.test.info().project.use.browserName;
if (typeof browserNameOrFunction === "function") {
import_test4.test.skip(browserNameOrFunction(browserName), reason);
} else {
import_test4.test.skip(browserName === browserNameOrFunction, reason);
}
},
mode: (mode, reason = `The functionality that is being tested is not applicable to ${mode} mode`) => {
import_test4.test.skip(import_test4.test.info().project.metadata.mode === mode, reason);
}
}
});
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
createConfig,
goto,
locator,
matchers,
setContent,
spyOnEvent,
test,
waitForChanges
});
//# sourceMappingURL=index.cjs.map