simple-playwright-framework 0.0.2

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.
Files changed (44) hide show
  1. package/dist/fixtures/auth.fixture.d.ts +2 -0
  2. package/dist/fixtures/auth.fixture.js +9 -0
  3. package/dist/fixtures/data.fixture.d.ts +5 -0
  4. package/dist/fixtures/data.fixture.js +35 -0
  5. package/dist/fixtures/envConfig.fixture.d.ts +7 -0
  6. package/dist/fixtures/envConfig.fixture.js +17 -0
  7. package/dist/fixtures/file.fixture.d.ts +7 -0
  8. package/dist/fixtures/file.fixture.js +10 -0
  9. package/dist/fixtures/index.d.ts +10 -0
  10. package/dist/fixtures/index.js +40 -0
  11. package/dist/fixtures/testrail.fixture.d.ts +5 -0
  12. package/dist/fixtures/testrail.fixture.js +10 -0
  13. package/dist/index.d.ts +5 -0
  14. package/dist/index.js +14 -0
  15. package/dist/loaders/data.loader.d.ts +8 -0
  16. package/dist/loaders/data.loader.js +50 -0
  17. package/dist/loaders/envConfig.loader.d.ts +2 -0
  18. package/dist/loaders/envConfig.loader.js +27 -0
  19. package/dist/loaders/scenario.loader.d.ts +2 -0
  20. package/dist/loaders/scenario.loader.js +78 -0
  21. package/dist/logger.d.ts +1 -0
  22. package/dist/logger.js +6 -0
  23. package/dist/types/auth.d.ts +9 -0
  24. package/dist/types/auth.js +2 -0
  25. package/dist/types/env.d.ts +12 -0
  26. package/dist/types/env.js +2 -0
  27. package/dist/types/fixtures.d.ts +12 -0
  28. package/dist/types/fixtures.js +2 -0
  29. package/dist/types/scenario.d.ts +5 -0
  30. package/dist/types/scenario.js +2 -0
  31. package/dist/types/testrail.d.ts +5 -0
  32. package/dist/types/testrail.js +2 -0
  33. package/dist/utils/auth-session/initAuthSession.d.ts +9 -0
  34. package/dist/utils/auth-session/initAuthSession.js +41 -0
  35. package/dist/utils/auth-session/storagePath.d.ts +1 -0
  36. package/dist/utils/auth-session/storagePath.js +14 -0
  37. package/dist/utils/auth-session/validateStore.d.ts +1 -0
  38. package/dist/utils/auth-session/validateStore.js +23 -0
  39. package/dist/utils/file-utils.d.ts +7 -0
  40. package/dist/utils/file-utils.js +34 -0
  41. package/dist/utils/testrail.client.d.ts +6 -0
  42. package/dist/utils/testrail.client.js +36 -0
  43. package/package.json +23 -0
  44. package/scripts/setup-framework.js +70 -0
@@ -0,0 +1,2 @@
1
+ import { AuthProvider } from "../types/auth";
2
+ export declare const basicAuthProvider: AuthProvider;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.basicAuthProvider = void 0;
4
+ // Example provider implementation
5
+ exports.basicAuthProvider = {
6
+ async login(page) {
7
+ // login steps...
8
+ }
9
+ };
@@ -0,0 +1,5 @@
1
+ import { TestInfo } from "@playwright/test";
2
+ import { Scenario } from "../types/scenario";
3
+ export declare const dataFixture: {
4
+ td: ({}: {}, use: (td: Scenario[] | object) => Promise<void>, testInfo: TestInfo) => Promise<void>;
5
+ };
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dataFixture = void 0;
4
+ const data_loader_1 = require("../loaders/data.loader");
5
+ exports.dataFixture = {
6
+ td: async ({}, use, testInfo) => {
7
+ const envName = process.env.TEST_ENV || "qa";
8
+ const tag = process.env.SCENARIO_TAG;
9
+ console.log("I am inside Fixture");
10
+ let td;
11
+ try {
12
+ td = (0, data_loader_1.loadTestData)(testInfo, envName);
13
+ console.log("Loaded test data:", td, "Type:", Array.isArray(td) ? "array" : typeof td);
14
+ }
15
+ catch (err) {
16
+ console.error(`❌ loadTestData threw for env '${envName}':`, err);
17
+ throw err;
18
+ }
19
+ if (!td || (Array.isArray(td) && td.length === 0)) {
20
+ throw new Error(`❌ No test data found for ${testInfo.file} in environment '${envName}'`);
21
+ }
22
+ // ✅ Only filter if data is an array of scenarios
23
+ if (Array.isArray(td)) {
24
+ td = td.filter(sc => !tag || sc.tags?.includes(tag));
25
+ if (td.length === 0) {
26
+ throw new Error(`❌ No scenarios found for env="${envName}" with tag="${tag}"`);
27
+ }
28
+ console.log(`✅ Loaded ${td.length} scenarios [env=${envName}${tag ? `, tag=${tag}` : ""}]`);
29
+ }
30
+ else {
31
+ console.log("✅ Loaded test data object (no tag filtering applied)");
32
+ }
33
+ await use(td);
34
+ },
35
+ };
@@ -0,0 +1,7 @@
1
+ import { Page } from "@playwright/test";
2
+ import type { EnvConfig } from "../types/env";
3
+ export declare const envConfigFixture: {
4
+ envConfig: ({ page }: {
5
+ page: Page;
6
+ }, use: (config: EnvConfig) => Promise<void>) => Promise<void>;
7
+ };
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.envConfigFixture = void 0;
4
+ const envConfig_loader_1 = require("../loaders/envConfig.loader");
5
+ exports.envConfigFixture = {
6
+ envConfig: async ({ page }, use) => {
7
+ const config = (0, envConfig_loader_1.loadConfig)();
8
+ /*
9
+ if (config.autoLaunch && config.baseUrl) {
10
+ console.log(`🌐 Navigating to: ${config.baseUrl}`);
11
+ await page.goto(config.baseUrl, { waitUntil: "domcontentloaded" });
12
+ console.log("✅ Navigation complete");
13
+ }
14
+ */
15
+ await use(config);
16
+ },
17
+ };
@@ -0,0 +1,7 @@
1
+ import { Page } from "@playwright/test";
2
+ import { FileUtils } from "../utils/file-utils";
3
+ export declare const fileFixture: {
4
+ fileUtils: ({ page }: {
5
+ page: Page;
6
+ }, use: (value: FileUtils) => Promise<void>) => Promise<void>;
7
+ };
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fileFixture = void 0;
4
+ const file_utils_1 = require("../utils/file-utils");
5
+ exports.fileFixture = {
6
+ fileUtils: async ({ page }, use) => {
7
+ const utils = new file_utils_1.FileUtils(page);
8
+ await use(utils);
9
+ },
10
+ };
@@ -0,0 +1,10 @@
1
+ import { Page } from "@playwright/test";
2
+ import { Fixtures } from "../types/fixtures";
3
+ export declare const test: import("@playwright/test").TestType<import("@playwright/test").PlaywrightTestArgs & import("@playwright/test").PlaywrightTestOptions & Fixtures & {
4
+ authStore: (page: Page, creds: {
5
+ username: string;
6
+ password: string;
7
+ }, providerRegistry: Record<string, any>) => Promise<void>;
8
+ }, import("@playwright/test").PlaywrightWorkerArgs & import("@playwright/test").PlaywrightWorkerOptions>;
9
+ export { expect } from "@playwright/test";
10
+ export * from "../loaders/scenario.loader";
@@ -0,0 +1,40 @@
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
+ exports.expect = exports.test = void 0;
18
+ const test_1 = require("@playwright/test");
19
+ const envConfig_fixture_1 = require("./envConfig.fixture");
20
+ const data_fixture_1 = require("./data.fixture");
21
+ const testrail_fixture_1 = require("./testrail.fixture");
22
+ const initAuthSession_1 = require("../utils/auth-session/initAuthSession");
23
+ const file_fixture_1 = require("./file.fixture");
24
+ exports.test = test_1.test.extend({
25
+ ...envConfig_fixture_1.envConfigFixture,
26
+ ...data_fixture_1.dataFixture,
27
+ ...testrail_fixture_1.testrailFixture,
28
+ ...file_fixture_1.fileFixture,
29
+ authStore: async ({ envConfig }, use) => {
30
+ await use(async (page, creds, providerRegistry) => {
31
+ if (!envConfig.authStorage) {
32
+ throw new Error(`[Framework] authStorage block missing in envConfig for this environment`);
33
+ }
34
+ await (0, initAuthSession_1.initAuthSession)(page, envConfig.authStorage, creds, providerRegistry);
35
+ });
36
+ },
37
+ });
38
+ var test_2 = require("@playwright/test");
39
+ Object.defineProperty(exports, "expect", { enumerable: true, get: function () { return test_2.expect; } });
40
+ __exportStar(require("../loaders/scenario.loader"), exports); // ✅ keep scenarioLoader export
@@ -0,0 +1,5 @@
1
+ import { TestRailClient } from "../utils/testrail.client";
2
+ import { TestFixture } from "@playwright/test";
3
+ export declare const testrailFixture: {
4
+ testrail: TestFixture<TestRailClient, {}>;
5
+ };
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.testrailFixture = void 0;
4
+ const testrail_client_1 = require("../utils/testrail.client");
5
+ exports.testrailFixture = {
6
+ testrail: async ({}, use) => {
7
+ const client = new testrail_client_1.TestRailClient(process.env.TESTRAIL_URL, process.env.TESTRAIL_USER, process.env.TESTRAIL_APIKEY);
8
+ await use(client);
9
+ },
10
+ };
@@ -0,0 +1,5 @@
1
+ export { initAuthSession } from "./utils/auth-session/initAuthSession";
2
+ export { test, expect } from "./fixtures/index";
3
+ export { scenarioLoader } from "./loaders/scenario.loader";
4
+ export { loadConfig } from "./loaders/envConfig.loader";
5
+ export { FileUtils } from "./utils/file-utils";
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FileUtils = exports.loadConfig = exports.scenarioLoader = exports.expect = exports.test = exports.initAuthSession = void 0;
4
+ var initAuthSession_1 = require("./utils/auth-session/initAuthSession");
5
+ Object.defineProperty(exports, "initAuthSession", { enumerable: true, get: function () { return initAuthSession_1.initAuthSession; } });
6
+ var index_1 = require("./fixtures/index");
7
+ Object.defineProperty(exports, "test", { enumerable: true, get: function () { return index_1.test; } });
8
+ Object.defineProperty(exports, "expect", { enumerable: true, get: function () { return index_1.expect; } });
9
+ var scenario_loader_1 = require("./loaders/scenario.loader");
10
+ Object.defineProperty(exports, "scenarioLoader", { enumerable: true, get: function () { return scenario_loader_1.scenarioLoader; } });
11
+ var envConfig_loader_1 = require("./loaders/envConfig.loader");
12
+ Object.defineProperty(exports, "loadConfig", { enumerable: true, get: function () { return envConfig_loader_1.loadConfig; } });
13
+ var file_utils_1 = require("./utils/file-utils");
14
+ Object.defineProperty(exports, "FileUtils", { enumerable: true, get: function () { return file_utils_1.FileUtils; } });
@@ -0,0 +1,8 @@
1
+ import { TestInfo } from "@playwright/test";
2
+ /**
3
+ * Loads environment-specific test data for a given test file.
4
+ * - Resolves JSON file path based on test file location
5
+ * - Validates existence, non-empty content, and JSON format
6
+ * - Returns environment-specific slice of data
7
+ */
8
+ export declare function loadTestData(testInfo: TestInfo, envName: string): any;
@@ -0,0 +1,50 @@
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.loadTestData = loadTestData;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ /**
10
+ * Loads environment-specific test data for a given test file.
11
+ * - Resolves JSON file path based on test file location
12
+ * - Validates existence, non-empty content, and JSON format
13
+ * - Returns environment-specific slice of data
14
+ */
15
+ function loadTestData(testInfo, envName) {
16
+ const projectRoot = process.cwd();
17
+ // Derive relative path from tests/ to the current test file
18
+ const rel = path_1.default.relative(path_1.default.join(projectRoot, "tests"), testInfo.file);
19
+ // Strip .spec.ts / .test.ts suffix
20
+ const fileBase = path_1.default.basename(rel)
21
+ .replace(/\.spec\.ts$/, "")
22
+ .replace(/\.test\.ts$/, "");
23
+ const dir = path_1.default.dirname(rel);
24
+ // Construct data file path
25
+ const dataPath = path_1.default.join(projectRoot, "data", dir, `${fileBase}.json`);
26
+ // Validate existence
27
+ if (!fs_1.default.existsSync(dataPath)) {
28
+ throw new Error(`\n❌ Test data file not found for test: ${fileBase}\nPath: ${dataPath}`);
29
+ }
30
+ // Validate non-empty
31
+ const raw = fs_1.default.readFileSync(dataPath, "utf-8").trim();
32
+ if (!raw) {
33
+ throw new Error(`\n❌ Test data file is empty for test: ${fileBase}\nPath: ${dataPath}`);
34
+ }
35
+ // Parse JSON
36
+ let parsed;
37
+ try {
38
+ parsed = JSON.parse(raw);
39
+ }
40
+ catch {
41
+ throw new Error(`\n❌ Invalid JSON format in test data for: ${fileBase}\nPath: ${dataPath}`);
42
+ }
43
+ // Return environment-specific slice
44
+ if (parsed[envName]) {
45
+ return parsed[envName];
46
+ }
47
+ else {
48
+ throw new Error(`\n❌ Data is not available for Execution Environment ${envName} in test data for: ${fileBase}\nPath: ${dataPath}`);
49
+ }
50
+ }
@@ -0,0 +1,2 @@
1
+ import type { EnvConfig } from "../types/env";
2
+ export declare function loadConfig(): EnvConfig;
@@ -0,0 +1,27 @@
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.loadConfig = loadConfig;
7
+ // framework/src/loaders/envConfig.loader.ts
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ function loadConfig() {
11
+ const configPath = path_1.default.join(process.cwd(), "config", "environments.json");
12
+ if (!fs_1.default.existsSync(configPath)) {
13
+ throw new Error(`❌ environments.json not found at ${configPath}`);
14
+ }
15
+ const raw = fs_1.default.readFileSync(configPath, "utf-8");
16
+ const allConfig = JSON.parse(raw);
17
+ const envName = process.env.TEST_ENV || "qa";
18
+ const defaults = allConfig.defaults || {};
19
+ const envConfig = allConfig[envName]; // ✅ flat lookup
20
+ if (!envConfig) {
21
+ throw new Error(`❌ Environment '${envName}' not defined in environments.json`);
22
+ }
23
+ if (!envConfig.baseUrl) {
24
+ throw new Error(`❌ Environment '${envName}' missing baseUrl`);
25
+ }
26
+ return { ...defaults, ...envConfig };
27
+ }
@@ -0,0 +1,2 @@
1
+ import { Scenario } from "../types/scenario";
2
+ export declare function scenarioLoader(testFile: string, env?: string, tag?: string): Scenario[];
@@ -0,0 +1,78 @@
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.scenarioLoader = scenarioLoader;
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ function scenarioLoader(testFile, env = process.env.TEST_ENV || "qa", tag = process.env.SCENARIO_TAG || "") {
40
+ // Replace "tests" with "data" and change extension
41
+ const relPath = testFile.replace("tests", "data").replace(/\.spec\.ts$/, ".json");
42
+ const scenarioFile = path.resolve(relPath);
43
+ if (!fs.existsSync(scenarioFile)) {
44
+ throw new Error(`❌ Scenario file not found: ${scenarioFile}`);
45
+ }
46
+ const raw = fs.readFileSync(scenarioFile, "utf-8").trim();
47
+ if (!raw) {
48
+ throw new Error(`❌ Scenario file is empty: ${scenarioFile}`);
49
+ }
50
+ let parsed;
51
+ try {
52
+ parsed = JSON.parse(raw);
53
+ }
54
+ catch {
55
+ throw new Error(`❌ Invalid JSON format in scenario file: ${scenarioFile}`);
56
+ }
57
+ let scenarios;
58
+ // If top-level is an array, return it directly
59
+ if (Array.isArray(parsed)) {
60
+ scenarios = parsed;
61
+ }
62
+ // If top-level is an object keyed by env, return the matching array
63
+ else if (parsed[env] && Array.isArray(parsed[env])) {
64
+ scenarios = parsed[env];
65
+ }
66
+ else {
67
+ throw new Error(`❌ Scenario file must contain an array or an object with environment key "${env}"`);
68
+ }
69
+ // ✅ Strict tag filtering
70
+ if (tag) {
71
+ scenarios = scenarios.filter(sc => sc.tags?.includes(tag));
72
+ if (scenarios.length === 0) {
73
+ throw new Error(`❌ No scenarios found for env="${env}" with tag="${tag}"`);
74
+ }
75
+ }
76
+ console.log(`✅ Loaded ${scenarios.length} scenarios [env=${env}${tag ? `, tag=${tag}` : ""}]`);
77
+ return scenarios;
78
+ }
@@ -0,0 +1 @@
1
+ export declare function log(message: string): void;
package/dist/logger.js ADDED
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.log = log;
4
+ function log(message) {
5
+ console.log('[Framework] ${message}');
6
+ }
@@ -0,0 +1,9 @@
1
+ import { Page } from "@playwright/test";
2
+ export interface AuthProvider {
3
+ login(page: Page): Promise<void>;
4
+ }
5
+ export interface AuthStorageConfig {
6
+ enabled: boolean;
7
+ validityMinutes: number;
8
+ provider: string;
9
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,12 @@
1
+ import type { AuthStorageConfig } from "./auth";
2
+ export interface EnvConfig {
3
+ baseUrl: string;
4
+ apiUrl?: string;
5
+ db?: {
6
+ host: string;
7
+ port: number;
8
+ user: string;
9
+ password: string;
10
+ };
11
+ authStorage?: AuthStorageConfig;
12
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,12 @@
1
+ import type { EnvConfig } from "./env";
2
+ import type { TestrailType } from "./testrail";
3
+ import type { FileUtils } from "../utils/file-utils";
4
+ /**
5
+ * Central fixture contract for Playwright test.extend
6
+ */
7
+ export type Fixtures = {
8
+ envConfig: EnvConfig;
9
+ td: any;
10
+ testrail: TestrailType;
11
+ fileUtils: FileUtils;
12
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,5 @@
1
+ export interface Scenario {
2
+ name: string;
3
+ [key: string]: any;
4
+ tags?: string[];
5
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,5 @@
1
+ import type { TestRailClient } from "../utils/testrail.client";
2
+ /**
3
+ * Type alias for TestRail client fixture
4
+ */
5
+ export type TestrailType = TestRailClient;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,9 @@
1
+ import { Page } from "@playwright/test";
2
+ import { AuthProvider, AuthStorageConfig } from "../../types/auth";
3
+ export declare function initAuthSession(page: Page, authStorage: AuthStorageConfig | undefined, creds: {
4
+ username: string;
5
+ password: string;
6
+ }, providerRegistry: Record<string, new (creds: {
7
+ username: string;
8
+ password: string;
9
+ }) => AuthProvider>): Promise<void>;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.initAuthSession = initAuthSession;
4
+ const storagePath_1 = require("./storagePath");
5
+ const validateStore_1 = require("./validateStore");
6
+ async function initAuthSession(page, authStorage, creds, providerRegistry) {
7
+ if (!authStorage?.enabled) {
8
+ console.log("[Framework] Auth storage disabled");
9
+ const ProviderClass = providerRegistry[authStorage?.provider ?? "OrangeHRMLogin"];
10
+ const authProvider = new ProviderClass(creds);
11
+ await authProvider.login(page);
12
+ return;
13
+ }
14
+ const { validityMinutes, provider } = authStorage;
15
+ const envName = process.env.TEST_ENV || "default";
16
+ const storagePath = (0, storagePath_1.getStoragePath)(provider, envName, creds.username);
17
+ const ProviderClass = providerRegistry[provider];
18
+ if (!ProviderClass)
19
+ throw new Error(`[Framework] Unknown provider: ${provider}`);
20
+ const authProvider = new ProviderClass(creds);
21
+ let needsLogin = true;
22
+ if ((0, validateStore_1.isAuthStoreValid)(storagePath, validityMinutes)) {
23
+ console.log(`[Framework] Found valid auth store: ${storagePath}`);
24
+ const state = JSON.parse(require("fs").readFileSync(storagePath, "utf-8"));
25
+ await page.context().addCookies(state.cookies);
26
+ await page.reload();
27
+ if (!page.url().includes("/auth/login")) {
28
+ console.log(`[Framework] Reusing valid auth store`);
29
+ needsLogin = false;
30
+ }
31
+ else {
32
+ console.log("[Framework] Auth store rejected, will re-login...");
33
+ }
34
+ }
35
+ if (needsLogin) {
36
+ console.log("[Framework] Performing fresh login...");
37
+ await authProvider.login(page);
38
+ await page.context().storageState({ path: storagePath });
39
+ console.log(`[Framework] New auth store created: ${storagePath}`);
40
+ }
41
+ }
@@ -0,0 +1 @@
1
+ export declare function getStoragePath(provider: string, envName: string, username: string): string;
@@ -0,0 +1,14 @@
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.getStoragePath = getStoragePath;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ function getStoragePath(provider, envName, username) {
10
+ const storageDir = path_1.default.resolve("storage");
11
+ if (!fs_1.default.existsSync(storageDir))
12
+ fs_1.default.mkdirSync(storageDir, { recursive: true });
13
+ return path_1.default.join(storageDir, `${provider}-${envName}-${username}-auth.json`);
14
+ }
@@ -0,0 +1 @@
1
+ export declare function isAuthStoreValid(storagePath: string, validityMinutes: number): boolean;
@@ -0,0 +1,23 @@
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.isAuthStoreValid = isAuthStoreValid;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ function isAuthStoreValid(storagePath, validityMinutes) {
9
+ if (!fs_1.default.existsSync(storagePath))
10
+ return false;
11
+ const raw = fs_1.default.readFileSync(storagePath, "utf-8").trim();
12
+ if (!raw)
13
+ return false;
14
+ try {
15
+ const state = JSON.parse(raw);
16
+ const stats = fs_1.default.statSync(storagePath);
17
+ const ageMinutes = (Date.now() - stats.mtimeMs) / 60000;
18
+ return (state.cookies?.length > 0 || state.origins?.length > 0) && ageMinutes < validityMinutes;
19
+ }
20
+ catch {
21
+ return false;
22
+ }
23
+ }
@@ -0,0 +1,7 @@
1
+ import { Page } from "@playwright/test";
2
+ export declare class FileUtils {
3
+ private page;
4
+ constructor(page: Page);
5
+ uploadFile(selector: string, filePath: string): Promise<void>;
6
+ downloadFile(selector: string, downloadDir?: string): Promise<string>;
7
+ }
@@ -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.FileUtils = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ class FileUtils {
10
+ page;
11
+ constructor(page) {
12
+ this.page = page;
13
+ }
14
+ // Upload a file by setting input[type=file]
15
+ async uploadFile(selector, filePath) {
16
+ const absolutePath = path_1.default.resolve(filePath);
17
+ if (!fs_1.default.existsSync(absolutePath)) {
18
+ throw new Error(`❌ File not found: ${absolutePath}`);
19
+ }
20
+ await this.page.setInputFiles(selector, absolutePath);
21
+ console.log(`✅ Uploaded file: ${absolutePath}`);
22
+ }
23
+ // Download a file triggered by clicking a link/button
24
+ async downloadFile(selector, downloadDir = "downloads") {
25
+ const downloadPromise = this.page.waitForEvent("download");
26
+ await this.page.click(selector);
27
+ const download = await downloadPromise;
28
+ const filePath = path_1.default.join(downloadDir, await download.suggestedFilename());
29
+ await download.saveAs(filePath);
30
+ console.log(`✅ File downloaded to: ${filePath}`);
31
+ return filePath;
32
+ }
33
+ }
34
+ exports.FileUtils = FileUtils;
@@ -0,0 +1,6 @@
1
+ export declare class TestRailClient {
2
+ private baseUrl;
3
+ private authHeader;
4
+ constructor(baseUrl?: string, username?: string, apiKey?: string);
5
+ addResult(caseId: number, statusId: number, comment: string): Promise<unknown>;
6
+ }
@@ -0,0 +1,36 @@
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.TestRailClient = void 0;
7
+ // framework/src/utils/testrail.client.ts
8
+ const node_fetch_1 = __importDefault(require("node-fetch"));
9
+ // framework/src/utils/testrail.client.ts
10
+ class TestRailClient {
11
+ baseUrl;
12
+ authHeader;
13
+ constructor(baseUrl, username, apiKey) {
14
+ this.baseUrl = baseUrl || "";
15
+ this.authHeader = username && apiKey
16
+ ? "Basic " + Buffer.from(`${username}:${apiKey}`).toString("base64")
17
+ : "";
18
+ }
19
+ async addResult(caseId, statusId, comment) {
20
+ if (!this.baseUrl) {
21
+ console.warn(`⚠️ Demo mode: would push result to TestRail case ${caseId} → status ${statusId}, comment: ${comment}`);
22
+ return { demo: true };
23
+ }
24
+ const url = `${this.baseUrl}/index.php?/api/v2/add_result_for_case/${caseId}`;
25
+ const response = await (0, node_fetch_1.default)(url, {
26
+ method: "POST",
27
+ headers: {
28
+ "Content-Type": "application/json",
29
+ "Authorization": this.authHeader,
30
+ },
31
+ body: JSON.stringify({ status_id: statusId, comment }),
32
+ });
33
+ return response.json();
34
+ }
35
+ }
36
+ exports.TestRailClient = TestRailClient;
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "simple-playwright-framework",
3
+ "version": "0.0.2",
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
6
+ "scripts": {
7
+ "clean": "rimraf dist .cache",
8
+ "build": "tsc -p tsconfig.json"
9
+ },
10
+ "dependencies": {
11
+ "node-fetch": "^3.3.2"
12
+ },
13
+ "peerDependencies": {
14
+ "@playwright/test": "^1.58.2"
15
+ },
16
+ "bin": {
17
+ "init-sample-project": "./scripts/setup-framework.js"
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "scripts"
22
+ ]
23
+ }
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env node
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import readline from "readline";
5
+
6
+ const rl = readline.createInterface({
7
+ input: process.stdin,
8
+ output: process.stdout,
9
+ });
10
+
11
+ function ask(question, defaultValue) {
12
+ return new Promise(resolve => {
13
+ rl.question(`${question} (${defaultValue}): `, answer => {
14
+ resolve(answer.trim() || defaultValue);
15
+ });
16
+ });
17
+ }
18
+
19
+ async function main() {
20
+ console.log("🚀 Init Sample Playwright Project");
21
+
22
+ const projectName = await ask("Project name", "sample-project");
23
+ const includeAuth = await ask("Include auth example? yes/no", "yes");
24
+ const includeData = await ask("Include data example? yes/no", "yes");
25
+
26
+ rl.close();
27
+
28
+ const projectDir = path.join(process.cwd(), projectName);
29
+ fs.mkdirSync(projectDir, { recursive: true });
30
+
31
+ // Config
32
+ fs.mkdirSync(path.join(projectDir, "config"), { recursive: true });
33
+ fs.writeFileSync(
34
+ path.join(projectDir, "config", "environments.json"),
35
+ JSON.stringify({ qa: { baseUrl: "http://localhost:3000" } }, null, 2)
36
+ );
37
+
38
+ // Tests
39
+ fs.mkdirSync(path.join(projectDir, "tests", "ui"), { recursive: true });
40
+ fs.writeFileSync(
41
+ path.join(projectDir, "tests", "ui", "login.spec.ts"),
42
+ `import { test, expect } from '@playwright/test';
43
+
44
+ test('login example', async ({ page }) => {
45
+ await page.goto('http://localhost:3000/login');
46
+ await expect(page).toHaveTitle(/Login/);
47
+ });`
48
+ );
49
+
50
+ if (includeAuth.toLowerCase() === "yes") {
51
+ fs.mkdirSync(path.join(projectDir, "auth"), { recursive: true });
52
+ fs.writeFileSync(
53
+ path.join(projectDir, "auth", "index.ts"),
54
+ `export const creds = { username: "testuser", password: "testpass" };`
55
+ );
56
+ }
57
+
58
+ if (includeData.toLowerCase() === "yes") {
59
+ fs.mkdirSync(path.join(projectDir, "data", "ui"), { recursive: true });
60
+ fs.writeFileSync(
61
+ path.join(projectDir, "data", "ui", "sample.txt"),
62
+ "Sample test data"
63
+ );
64
+ }
65
+
66
+ console.log(`✅ Sample project created in ${projectDir}`);
67
+ console.log(`👉 Next steps:\n cd ${projectName}\n npx playwright test`);
68
+ }
69
+
70
+ main();