pw-core 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +579 -0
- package/dist/component/table.d.ts +48 -0
- package/dist/component/table.js +112 -0
- package/dist/helpers.d.ts +15 -0
- package/dist/helpers.js +39 -0
- package/dist/page/actions/locator-actions.d.ts +10 -0
- package/dist/page/actions/locator-actions.js +60 -0
- package/dist/page/actions/page-actions.d.ts +39 -0
- package/dist/page/actions/page-actions.js +107 -0
- package/dist/page/assertions/expect.d.ts +2 -0
- package/dist/page/assertions/expect.js +7 -0
- package/dist/page/assertions/verify-chain.d.ts +63 -0
- package/dist/page/assertions/verify-chain.js +59 -0
- package/dist/page/assertions/verify-helpers.d.ts +22 -0
- package/dist/page/assertions/verify-helpers.js +28 -0
- package/dist/page/config.d.ts +31 -0
- package/dist/page/config.js +26 -0
- package/dist/page/index.d.ts +10 -0
- package/dist/page/index.js +26 -0
- package/dist/page/locators/resolver.d.ts +23 -0
- package/dist/page/locators/resolver.js +83 -0
- package/dist/page/registry.d.ts +20 -0
- package/dist/page/registry.js +34 -0
- package/dist/page/typed-page.d.ts +78 -0
- package/dist/page/typed-page.js +78 -0
- package/dist/page/types/proxy-methods.d.ts +53 -0
- package/dist/page/types/proxy-methods.js +2 -0
- package/dist/page/utils/formatter.d.ts +3 -0
- package/dist/page/utils/formatter.js +50 -0
- package/package.json +61 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./config"), exports);
|
|
18
|
+
__exportStar(require("./utils/formatter"), exports);
|
|
19
|
+
__exportStar(require("./assertions/verify-chain"), exports);
|
|
20
|
+
__exportStar(require("./assertions/verify-helpers"), exports);
|
|
21
|
+
__exportStar(require("./assertions/expect"), exports);
|
|
22
|
+
__exportStar(require("./actions/page-actions"), exports);
|
|
23
|
+
__exportStar(require("./actions/locator-actions"), exports);
|
|
24
|
+
__exportStar(require("./locators/resolver"), exports);
|
|
25
|
+
__exportStar(require("./typed-page"), exports);
|
|
26
|
+
__exportStar(require("./registry"), exports);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Page, Locator } from '@playwright/test';
|
|
2
|
+
import { ChainedKeys, PageKeys } from '../config';
|
|
3
|
+
export declare function defineLocators<T extends {
|
|
4
|
+
testIds?: Record<string, string>;
|
|
5
|
+
selectors?: Record<string, string>;
|
|
6
|
+
}>(instance: any, context: Page | Locator, config: T): void;
|
|
7
|
+
export declare function resolveLocator<T extends {
|
|
8
|
+
testIds?: Record<string, string>;
|
|
9
|
+
selectors?: Record<string, string>;
|
|
10
|
+
}>(context: Page | Locator, config: T, target: PageKeys<T> | ChainedKeys<T> | Locator, options?: {
|
|
11
|
+
nth?: number;
|
|
12
|
+
raw?: boolean;
|
|
13
|
+
}): Locator;
|
|
14
|
+
/**
|
|
15
|
+
* Resolve a config key to a proxied Locator with step-wrapped Playwright methods.
|
|
16
|
+
*/
|
|
17
|
+
export declare function locator<T extends {
|
|
18
|
+
testIds?: Record<string, string>;
|
|
19
|
+
selectors?: Record<string, string>;
|
|
20
|
+
}>(context: Page | Locator, config: T, target: PageKeys<T> | ChainedKeys<T> | Locator, options?: Parameters<Locator['filter']>[0] & {
|
|
21
|
+
nth?: number;
|
|
22
|
+
}): Locator;
|
|
23
|
+
export declare function wrapLocatorWithProxy(loc: Locator): Locator;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defineLocators = defineLocators;
|
|
4
|
+
exports.resolveLocator = resolveLocator;
|
|
5
|
+
exports.locator = locator;
|
|
6
|
+
exports.wrapLocatorWithProxy = wrapLocatorWithProxy;
|
|
7
|
+
const test_1 = require("@playwright/test");
|
|
8
|
+
const config_1 = require("../config");
|
|
9
|
+
const formatter_1 = require("../utils/formatter");
|
|
10
|
+
function defineLocators(instance, context, config) {
|
|
11
|
+
if (config.testIds) {
|
|
12
|
+
for (const key of Object.keys(config.testIds)) {
|
|
13
|
+
Object.defineProperty(instance, key, {
|
|
14
|
+
get: () => {
|
|
15
|
+
return context.getByTestId(config.testIds[key]);
|
|
16
|
+
},
|
|
17
|
+
enumerable: true,
|
|
18
|
+
configurable: true,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
if (config.selectors) {
|
|
23
|
+
for (const key of Object.keys(config.selectors)) {
|
|
24
|
+
Object.defineProperty(instance, key, {
|
|
25
|
+
get: () => {
|
|
26
|
+
return context.locator(config.selectors[key]);
|
|
27
|
+
},
|
|
28
|
+
enumerable: true,
|
|
29
|
+
configurable: true,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function resolveLocator(context, config, target, options) {
|
|
35
|
+
if (typeof target !== 'string')
|
|
36
|
+
return target;
|
|
37
|
+
const targetStr = target;
|
|
38
|
+
const parts = targetStr.split('.');
|
|
39
|
+
const resolveSingle = (key, ctx) => {
|
|
40
|
+
if (config.testIds && key in config.testIds) {
|
|
41
|
+
return ctx.getByTestId(config.testIds[key]);
|
|
42
|
+
}
|
|
43
|
+
if (config.selectors && key in config.selectors) {
|
|
44
|
+
return ctx.locator(config.selectors[key]);
|
|
45
|
+
}
|
|
46
|
+
throw new Error(`Locator key '${key}' is not defined in testIds or selectors.`);
|
|
47
|
+
};
|
|
48
|
+
let loc = resolveSingle(parts[0], context);
|
|
49
|
+
for (let i = 1; i < parts.length; i++) {
|
|
50
|
+
loc = resolveSingle(parts[i], loc);
|
|
51
|
+
}
|
|
52
|
+
if (options?.raw) {
|
|
53
|
+
return loc;
|
|
54
|
+
}
|
|
55
|
+
if (options?.nth !== undefined) {
|
|
56
|
+
return loc.nth(options.nth);
|
|
57
|
+
}
|
|
58
|
+
return loc.first();
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Resolve a config key to a proxied Locator with step-wrapped Playwright methods.
|
|
62
|
+
*/
|
|
63
|
+
function locator(context, config, target, options) {
|
|
64
|
+
const resolved = resolveLocator(context, config, target, { raw: true });
|
|
65
|
+
const filtered = options ? resolved.filter(options) : resolved;
|
|
66
|
+
const loc = (options && typeof options === 'object' && 'nth' in options && options.nth !== undefined) ? filtered.nth(options.nth) : filtered;
|
|
67
|
+
return wrapLocatorWithProxy(loc);
|
|
68
|
+
}
|
|
69
|
+
function wrapLocatorWithProxy(loc) {
|
|
70
|
+
const actionMethods = [...config_1.zeroArgMethodsList, ...config_1.oneArgMethodsList];
|
|
71
|
+
return new Proxy(loc, {
|
|
72
|
+
get: (targetLoc, propKey, receiver) => {
|
|
73
|
+
const val = Reflect.get(targetLoc, propKey, receiver);
|
|
74
|
+
if (typeof val === 'function' && actionMethods.includes(propKey)) {
|
|
75
|
+
return (...args) => {
|
|
76
|
+
const stepName = (0, formatter_1.formatStepDescription)(propKey, targetLoc, args);
|
|
77
|
+
return test_1.test.step(stepName, () => val.apply(targetLoc, args));
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
return val;
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import test, { Page, TestType } from '@playwright/test';
|
|
2
|
+
import { PageConfig } from './config';
|
|
3
|
+
import { TypedPage } from './typed-page';
|
|
4
|
+
export type PageRegistry = Record<string, PageConfig>;
|
|
5
|
+
export interface PageConstructor<C extends PageConfig<any>> {
|
|
6
|
+
new (page: Page): TypedPage<C>;
|
|
7
|
+
}
|
|
8
|
+
export interface PageRegistryObject<T extends Record<string, PageConfig<any>>> {
|
|
9
|
+
classes: {
|
|
10
|
+
[K in keyof T]: PageConstructor<T[K]>;
|
|
11
|
+
};
|
|
12
|
+
extend<O extends Partial<{
|
|
13
|
+
[K in keyof T]: new (page: Page, ...args: any[]) => any;
|
|
14
|
+
}>, B extends TestType<any, any> = typeof test>(overrides: O, base?: B): B extends TestType<infer P, infer W> ? TestType<P & {
|
|
15
|
+
[K in keyof T]: K extends keyof O ? O[K] extends new (...args: any[]) => infer R ? R : TypedPage<T[K]> : TypedPage<T[K]>;
|
|
16
|
+
}, W> : never;
|
|
17
|
+
}
|
|
18
|
+
export declare function createPageRegistry<T extends {
|
|
19
|
+
[K in keyof T]: PageConfig<T[K]>;
|
|
20
|
+
} & Record<string, PageConfig<any>>>(registry: T): PageRegistryObject<T>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createPageRegistry = createPageRegistry;
|
|
7
|
+
const test_1 = __importDefault(require("@playwright/test"));
|
|
8
|
+
const typed_page_1 = require("./typed-page");
|
|
9
|
+
function createPageRegistry(registry) {
|
|
10
|
+
const classes = {};
|
|
11
|
+
for (const key of Object.keys(registry)) {
|
|
12
|
+
const config = registry[key];
|
|
13
|
+
classes[key] = config.Class || (class extends typed_page_1.TypedPage {
|
|
14
|
+
constructor(page) {
|
|
15
|
+
super(page, config);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
classes,
|
|
21
|
+
extend(overrides, base = test_1.default) {
|
|
22
|
+
const fixtures = {};
|
|
23
|
+
for (const key of Object.keys(registry)) {
|
|
24
|
+
fixtures[key] = async ({ page }, use) => {
|
|
25
|
+
const config = registry[key];
|
|
26
|
+
const PageClass = overrides[key] || classes[key];
|
|
27
|
+
const instance = new PageClass(page);
|
|
28
|
+
await use(instance);
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return base.extend(fixtures);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Page, Locator, expect as playwrightExpect } from '@playwright/test';
|
|
2
|
+
import { TypedLocators, ProxyLocatorMethods, PageKeys, ChainedKeys } from './config';
|
|
3
|
+
import { VerifyFn, AssertionsMethod } from './assertions/verify-chain';
|
|
4
|
+
declare class TypedPageClass<T extends {
|
|
5
|
+
testIds?: Record<string, string>;
|
|
6
|
+
selectors?: Record<string, string>;
|
|
7
|
+
url?: string;
|
|
8
|
+
}> {
|
|
9
|
+
readonly page: Page;
|
|
10
|
+
readonly context: Page | Locator;
|
|
11
|
+
protected readonly config: T;
|
|
12
|
+
readonly url?: string;
|
|
13
|
+
timeout?: number;
|
|
14
|
+
constructor(context: Page | Locator, config: T, options?: {
|
|
15
|
+
timeout?: number;
|
|
16
|
+
});
|
|
17
|
+
/** Navigate to the URL from page config. */
|
|
18
|
+
goto(options?: {
|
|
19
|
+
referrer?: string;
|
|
20
|
+
timeout?: number;
|
|
21
|
+
waitUntil?: 'load' | 'domcontentloaded' | 'networkidle' | 'commit';
|
|
22
|
+
}): Promise<void>;
|
|
23
|
+
/** Assert the current URL matches the page config URL or a custom pattern. */
|
|
24
|
+
verifyURL(urlOrOptions?: string | RegExp | Parameters<ReturnType<typeof playwrightExpect<Page>>['toHaveURL']>[1], options?: Parameters<ReturnType<typeof playwrightExpect<Page>>['toHaveURL']>[1]): Promise<void>;
|
|
25
|
+
/** Assert the page title matches the expected value. */
|
|
26
|
+
verifyTitle(title: string | RegExp, options?: Parameters<ReturnType<typeof playwrightExpect<Page>>['toHaveTitle']>[1]): Promise<void>;
|
|
27
|
+
/** Reload the current page. */
|
|
28
|
+
reload(options?: Parameters<Page['reload']>[0]): Promise<void>;
|
|
29
|
+
/** Wait for the page to reach a load state. */
|
|
30
|
+
waitForLoadState(state?: Parameters<Page['waitForLoadState']>[0], options?: Parameters<Page['waitForLoadState']>[1]): Promise<void>;
|
|
31
|
+
/** Wait for navigation to a URL. Defaults to the page config URL. */
|
|
32
|
+
waitForURL(urlOrOptions?: string | RegExp | Parameters<Page['waitForURL']>[1], options?: Parameters<Page['waitForURL']>[1]): Promise<void>;
|
|
33
|
+
resolveLocator(target: PageKeys<T> | ChainedKeys<T> | Locator, options?: {
|
|
34
|
+
nth?: number;
|
|
35
|
+
raw?: boolean;
|
|
36
|
+
}): Locator;
|
|
37
|
+
/** Resolve a config key to a proxied Locator with step-wrapped methods. */
|
|
38
|
+
locator(target: PageKeys<T> | ChainedKeys<T> | Locator, options?: Parameters<Locator['filter']>[0] & {
|
|
39
|
+
nth?: number;
|
|
40
|
+
}): Locator;
|
|
41
|
+
/** Chainable assertions on config keys. Supports `.soft` for soft assertions. */
|
|
42
|
+
get verify(): VerifyFn<T> & {
|
|
43
|
+
soft: VerifyFn<T>;
|
|
44
|
+
};
|
|
45
|
+
verifyHidden(target: PageKeys<T> | ChainedKeys<T> | Locator, options?: Parameters<ReturnType<typeof playwrightExpect<Locator>>['toBeHidden']>[0] & {
|
|
46
|
+
nth?: number;
|
|
47
|
+
message?: string;
|
|
48
|
+
}): Promise<void>;
|
|
49
|
+
verifyEnabled(target: PageKeys<T> | ChainedKeys<T> | Locator, options?: Parameters<ReturnType<typeof playwrightExpect<Locator>>['toBeEnabled']>[0] & {
|
|
50
|
+
nth?: number;
|
|
51
|
+
message?: string;
|
|
52
|
+
}): Promise<void>;
|
|
53
|
+
verifyDisabled(target: PageKeys<T> | ChainedKeys<T> | Locator, options?: Parameters<ReturnType<typeof playwrightExpect<Locator>>['toBeDisabled']>[0] & {
|
|
54
|
+
nth?: number;
|
|
55
|
+
message?: string;
|
|
56
|
+
}): Promise<void>;
|
|
57
|
+
expect(target: PageKeys<T> | ChainedKeys<T> | Locator, message?: string): ReturnType<typeof playwrightExpect<Locator>>;
|
|
58
|
+
}
|
|
59
|
+
export type TypedPageType<T extends {
|
|
60
|
+
testIds?: Record<string, string>;
|
|
61
|
+
selectors?: Record<string, string>;
|
|
62
|
+
url?: string;
|
|
63
|
+
}> = TypedPageClass<T> & TypedLocators<T> & ProxyLocatorMethods<T> & AssertionsMethod<T>;
|
|
64
|
+
export declare const TypedPage: {
|
|
65
|
+
new <T extends {
|
|
66
|
+
testIds?: Record<string, string>;
|
|
67
|
+
selectors?: Record<string, string>;
|
|
68
|
+
url?: string;
|
|
69
|
+
}>(context: Page | Locator, config: T, options?: {
|
|
70
|
+
timeout?: number;
|
|
71
|
+
}): TypedPageType<T>;
|
|
72
|
+
};
|
|
73
|
+
export type TypedPage<T extends {
|
|
74
|
+
testIds?: Record<string, string>;
|
|
75
|
+
selectors?: Record<string, string>;
|
|
76
|
+
url?: string;
|
|
77
|
+
}> = TypedPageType<T>;
|
|
78
|
+
export {};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TypedPage = void 0;
|
|
4
|
+
const verify_chain_1 = require("./assertions/verify-chain");
|
|
5
|
+
const verify_helpers_1 = require("./assertions/verify-helpers");
|
|
6
|
+
const expect_1 = require("./assertions/expect");
|
|
7
|
+
const page_actions_1 = require("./actions/page-actions");
|
|
8
|
+
const locator_actions_1 = require("./actions/locator-actions");
|
|
9
|
+
const resolver_1 = require("./locators/resolver");
|
|
10
|
+
class TypedPageClass {
|
|
11
|
+
page;
|
|
12
|
+
context;
|
|
13
|
+
config;
|
|
14
|
+
url;
|
|
15
|
+
timeout;
|
|
16
|
+
constructor(context, config, options) {
|
|
17
|
+
this.context = context;
|
|
18
|
+
this.page = typeof context.page === 'function'
|
|
19
|
+
? context.page()
|
|
20
|
+
: context;
|
|
21
|
+
this.config = config;
|
|
22
|
+
this.url = config.url;
|
|
23
|
+
this.timeout = options?.timeout;
|
|
24
|
+
(0, resolver_1.defineLocators)(this, this.context, this.config);
|
|
25
|
+
(0, locator_actions_1.defineActionMethods)(this, this.resolveLocator.bind(this), this.timeout);
|
|
26
|
+
}
|
|
27
|
+
/** Navigate to the URL from page config. */
|
|
28
|
+
async goto(options) {
|
|
29
|
+
return (0, page_actions_1.goto)(this.page, this.url, this.constructor.name, options);
|
|
30
|
+
}
|
|
31
|
+
/** Assert the current URL matches the page config URL or a custom pattern. */
|
|
32
|
+
async verifyURL(urlOrOptions, options) {
|
|
33
|
+
return (0, page_actions_1.verifyURL)(this.page, this.url, this.constructor.name, urlOrOptions, options);
|
|
34
|
+
}
|
|
35
|
+
/** Assert the page title matches the expected value. */
|
|
36
|
+
async verifyTitle(title, options) {
|
|
37
|
+
return (0, page_actions_1.verifyTitle)(this.page, title, options);
|
|
38
|
+
}
|
|
39
|
+
/** Reload the current page. */
|
|
40
|
+
async reload(options) {
|
|
41
|
+
return (0, page_actions_1.reload)(this.page, options);
|
|
42
|
+
}
|
|
43
|
+
/** Wait for the page to reach a load state. */
|
|
44
|
+
async waitForLoadState(state, options) {
|
|
45
|
+
return (0, page_actions_1.waitForLoadState)(this.page, state, options);
|
|
46
|
+
}
|
|
47
|
+
/** Wait for navigation to a URL. Defaults to the page config URL. */
|
|
48
|
+
async waitForURL(urlOrOptions, options) {
|
|
49
|
+
return (0, page_actions_1.waitForURL)(this.page, this.url, this.constructor.name, urlOrOptions, options);
|
|
50
|
+
}
|
|
51
|
+
resolveLocator(target, options) {
|
|
52
|
+
return (0, resolver_1.resolveLocator)(this.context, this.config, target, options);
|
|
53
|
+
}
|
|
54
|
+
/** Resolve a config key to a proxied Locator with step-wrapped methods. */
|
|
55
|
+
locator(target, options) {
|
|
56
|
+
return (0, resolver_1.locator)(this.context, this.config, target, options);
|
|
57
|
+
}
|
|
58
|
+
/** Chainable assertions on config keys. Supports `.soft` for soft assertions. */
|
|
59
|
+
get verify() {
|
|
60
|
+
const fn = (target, opts) => (0, verify_chain_1.createVerifyChain)(this.resolveLocator.bind(this), target, opts, false);
|
|
61
|
+
fn.soft = (target, opts) => (0, verify_chain_1.createVerifyChain)(this.resolveLocator.bind(this), target, opts, true);
|
|
62
|
+
return fn;
|
|
63
|
+
}
|
|
64
|
+
async verifyHidden(target, options) {
|
|
65
|
+
return (0, verify_helpers_1.verifyHidden)(this.resolveLocator.bind(this), target, options);
|
|
66
|
+
}
|
|
67
|
+
async verifyEnabled(target, options) {
|
|
68
|
+
return (0, verify_helpers_1.verifyEnabled)(this.resolveLocator.bind(this), target, options);
|
|
69
|
+
}
|
|
70
|
+
async verifyDisabled(target, options) {
|
|
71
|
+
return (0, verify_helpers_1.verifyDisabled)(this.resolveLocator.bind(this), target, options);
|
|
72
|
+
}
|
|
73
|
+
expect(target, message) {
|
|
74
|
+
const resolved = typeof target !== 'string' ? target : this.resolveLocator(target);
|
|
75
|
+
return (0, expect_1.typedExpect)(resolved, message);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
exports.TypedPage = TypedPageClass;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Locator } from '@playwright/test';
|
|
2
|
+
import type { PageKeys, ChainedKeys } from '../config';
|
|
3
|
+
/**
|
|
4
|
+
* Playwright Locator methods proxied onto the page object.
|
|
5
|
+
* Each method resolves a config key (or Locator) as the first argument.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* await page.click('submit');
|
|
10
|
+
* await page.fill('username', 'alice', { nth: 1 });
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* pw-core adds `nth` to options — targets a specific match when multiple elements resolve.
|
|
14
|
+
*/
|
|
15
|
+
type ProxyKeys = 'click' | 'dblclick' | 'hover' | 'focus' | 'blur' | 'check' | 'uncheck' | 'clear' | 'waitFor' | 'isChecked' | 'isDisabled' | 'isVisible' | 'textContent' | 'innerText' | 'allInnerTexts' | 'allTextContents' | 'count' | 'scrollIntoViewIfNeeded' | 'boundingBox' | 'press' | 'pressSequentially' | 'selectOption' | 'setInputFiles' | 'getAttribute';
|
|
16
|
+
type ModifyProxyArgs<Args extends any[]> = Args extends [] ? [options?: {
|
|
17
|
+
nth?: number;
|
|
18
|
+
}] : Args extends [any, any?] ? [Args[0], (Exclude<Args[1], undefined> & {
|
|
19
|
+
nth?: number;
|
|
20
|
+
})?] : Args extends [any?] ? Exclude<Args[0], undefined> extends object ? [(Exclude<Args[0], undefined> & {
|
|
21
|
+
nth?: number;
|
|
22
|
+
})?] : [Exclude<Args[0], undefined>, options?: {
|
|
23
|
+
nth?: number;
|
|
24
|
+
}] : [options?: {
|
|
25
|
+
nth?: number;
|
|
26
|
+
}];
|
|
27
|
+
type BaseProxyLocatorMethods<T> = {
|
|
28
|
+
[K in keyof Locator as K extends ProxyKeys ? K : never]: (target: PageKeys<T> | ChainedKeys<T> | Locator, ...args: ModifyProxyArgs<Parameters<Locator[K]>>) => ReturnType<Locator[K]>;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Playwright Locator methods proxied onto the page object.
|
|
32
|
+
* Each method resolves a config key (or Locator) as the first argument.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* await page.click('submit');
|
|
37
|
+
* await page.fill('username', 'alice', { nth: 1 });
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* pw-core adds `nth` to options — targets a specific match when multiple elements resolve.
|
|
41
|
+
*/
|
|
42
|
+
export interface ProxyLocatorMethods<T> extends BaseProxyLocatorMethods<T> {
|
|
43
|
+
/** @see {@link https://playwright.dev/docs/api/class-locator#locator-fill Locator.fill} */
|
|
44
|
+
fill(target: PageKeys<T> | ChainedKeys<T> | Locator, value: string, options?: Parameters<Locator['fill']>[1] & {
|
|
45
|
+
nth?: number;
|
|
46
|
+
mask?: boolean;
|
|
47
|
+
}): Promise<void>;
|
|
48
|
+
/** @see {@link https://playwright.dev/docs/api/class-locator#locator-drag-to Locator.dragTo} */
|
|
49
|
+
dragTo(target: PageKeys<T> | ChainedKeys<T> | Locator, destination: PageKeys<T> | ChainedKeys<T> | Locator, options?: Parameters<Locator['dragTo']>[1] & {
|
|
50
|
+
nth?: number;
|
|
51
|
+
}): Promise<void>;
|
|
52
|
+
}
|
|
53
|
+
export {};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare function formatTarget(target: any): string;
|
|
2
|
+
export declare function formatStepDescription(methodName: string, target: any, args: any[]): string;
|
|
3
|
+
export declare function formatAssertionDescription(target: any, matcherName: string, isNegated: boolean, args: any[]): string;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatTarget = formatTarget;
|
|
4
|
+
exports.formatStepDescription = formatStepDescription;
|
|
5
|
+
exports.formatAssertionDescription = formatAssertionDescription;
|
|
6
|
+
function formatTarget(target) {
|
|
7
|
+
if (typeof target === 'string')
|
|
8
|
+
return target;
|
|
9
|
+
if (target && typeof target.toString === 'function') {
|
|
10
|
+
return target.toString().replace(/^Locator@/, '');
|
|
11
|
+
}
|
|
12
|
+
return 'locator';
|
|
13
|
+
}
|
|
14
|
+
function formatStepDescription(methodName, target, args) {
|
|
15
|
+
const targetStr = formatTarget(target);
|
|
16
|
+
if (methodName === 'fill' && args[0] !== undefined) {
|
|
17
|
+
const options = args[1];
|
|
18
|
+
const shouldMask = !(options && typeof options === 'object' && options.mask === false);
|
|
19
|
+
const displayValue = shouldMask ? '*'.repeat(String(args[0]).length) : args[0];
|
|
20
|
+
return `Fill "${targetStr}" with "${displayValue}"`;
|
|
21
|
+
}
|
|
22
|
+
if (methodName === 'click')
|
|
23
|
+
return `Click "${targetStr}"`;
|
|
24
|
+
if (methodName === 'dblclick')
|
|
25
|
+
return `Double-click "${targetStr}"`;
|
|
26
|
+
if (methodName === 'dragTo')
|
|
27
|
+
return `Drag "${targetStr}" to "${formatTarget(args[0])}"`;
|
|
28
|
+
if (methodName === 'check')
|
|
29
|
+
return `Check "${targetStr}"`;
|
|
30
|
+
if (methodName === 'uncheck')
|
|
31
|
+
return `Uncheck "${targetStr}"`;
|
|
32
|
+
const formattedMethod = methodName.charAt(0).toUpperCase() + methodName.slice(1);
|
|
33
|
+
const cleanArgs = args.filter(a => typeof a === 'string' || typeof a === 'number').map(a => `"${a}"`).join(', ');
|
|
34
|
+
return `${formattedMethod} "${targetStr}"${cleanArgs ? ' with ' + cleanArgs : ''}`;
|
|
35
|
+
}
|
|
36
|
+
function formatAssertionDescription(target, matcherName, isNegated, args) {
|
|
37
|
+
const targetStr = formatTarget(target);
|
|
38
|
+
const prefix = `Verify "${targetStr}"`;
|
|
39
|
+
const matcherMap = {
|
|
40
|
+
toBeVisible: isNegated ? 'is not visible' : 'is visible',
|
|
41
|
+
toBeHidden: isNegated ? 'is not hidden' : 'is hidden',
|
|
42
|
+
toBeEnabled: isNegated ? 'is not enabled' : 'is enabled',
|
|
43
|
+
toBeDisabled: isNegated ? 'is not disabled' : 'is disabled',
|
|
44
|
+
toBeChecked: isNegated ? 'is not checked' : 'is checked',
|
|
45
|
+
toHaveText: (isNegated ? 'does not have text "' : 'has text "') + args.map(a => String(a)).join(', ') + '"',
|
|
46
|
+
toContainText: (isNegated ? 'does not contain "' : 'contains "') + args.map(a => String(a)).join(', ') + '"',
|
|
47
|
+
toHaveCount: (isNegated ? 'does not have count of "' : 'has count of "') + args[0] + '"'
|
|
48
|
+
};
|
|
49
|
+
return `${prefix} ${matcherMap[matcherName] || `${isNegated ? 'not ' : ''}${matcherName}`}`;
|
|
50
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pw-core",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Developer-first Playwright framework with typed page objects, components, assertions, and reusable automation utilities.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Shanmuka Chandra Teja Anem",
|
|
7
|
+
"url": "https://github.com/shanmukaanem"
|
|
8
|
+
},
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"keywords": [
|
|
11
|
+
"playwright",
|
|
12
|
+
"automation",
|
|
13
|
+
"testing",
|
|
14
|
+
"e2e",
|
|
15
|
+
"qa",
|
|
16
|
+
"sdet",
|
|
17
|
+
"page-object",
|
|
18
|
+
"framework",
|
|
19
|
+
"typescript"
|
|
20
|
+
],
|
|
21
|
+
"main": "./dist/index.js",
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"README.md",
|
|
26
|
+
"LICENSE"
|
|
27
|
+
],
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"default": "./dist/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./page": {
|
|
34
|
+
"types": "./dist/page/index.d.ts",
|
|
35
|
+
"default": "./dist/page/index.js"
|
|
36
|
+
},
|
|
37
|
+
"./component/table": {
|
|
38
|
+
"types": "./dist/component/table.d.ts",
|
|
39
|
+
"default": "./dist/component/table.js"
|
|
40
|
+
},
|
|
41
|
+
"./helpers": {
|
|
42
|
+
"types": "./dist/helpers.d.ts",
|
|
43
|
+
"default": "./dist/helpers.js"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "rimraf dist && tsc",
|
|
48
|
+
"prepare": "npm run build"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"@playwright/test": "^1.40.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@playwright/test": "^1.60.0",
|
|
55
|
+
"rimraf": "^6.0.1",
|
|
56
|
+
"typescript": "^5.3.3"
|
|
57
|
+
},
|
|
58
|
+
"publishConfig": {
|
|
59
|
+
"access": "public"
|
|
60
|
+
}
|
|
61
|
+
}
|