simple-playwright-framework 0.0.17 → 0.0.18

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/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { initAuthSession } from "./utils/auth-session/initAuthSession";
2
+ export { initApiAuthSession } from "./utils/auth-session/initApiAuthSession";
2
3
  export { test, expect } from "./fixtures/index";
3
4
  export { scenarioLoader } from "./loaders/scenario.loader";
4
5
  export { loadConfig } from "./loaders/envConfig.loader";
@@ -7,4 +8,4 @@ export { envConfigFixture } from "./fixtures/envConfig.fixture";
7
8
  export { dataFixture } from "./fixtures/data.fixture";
8
9
  export { fileFixture } from "./fixtures/file.fixture";
9
10
  export { testrailFixture } from "./fixtures/testrail.fixture";
10
- export type { AuthProvider, AuthStorageConfig } from "./types/auth";
11
+ export type { AuthProvider, ApiAuthProvider, AuthStorageConfig } from "./types/auth";
package/dist/index.js CHANGED
@@ -1,8 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.testrailFixture = exports.fileFixture = exports.dataFixture = exports.envConfigFixture = exports.FileUtils = exports.loadConfig = exports.scenarioLoader = exports.expect = exports.test = exports.initAuthSession = void 0;
3
+ exports.testrailFixture = exports.fileFixture = exports.dataFixture = exports.envConfigFixture = exports.FileUtils = exports.loadConfig = exports.scenarioLoader = exports.expect = exports.test = exports.initApiAuthSession = exports.initAuthSession = void 0;
4
4
  var initAuthSession_1 = require("./utils/auth-session/initAuthSession");
5
5
  Object.defineProperty(exports, "initAuthSession", { enumerable: true, get: function () { return initAuthSession_1.initAuthSession; } });
6
+ var initApiAuthSession_1 = require("./utils/auth-session/initApiAuthSession");
7
+ Object.defineProperty(exports, "initApiAuthSession", { enumerable: true, get: function () { return initApiAuthSession_1.initApiAuthSession; } });
6
8
  var index_1 = require("./fixtures/index");
7
9
  Object.defineProperty(exports, "test", { enumerable: true, get: function () { return index_1.test; } });
8
10
  Object.defineProperty(exports, "expect", { enumerable: true, get: function () { return index_1.expect; } });
@@ -2,6 +2,9 @@ import { Page } from "@playwright/test";
2
2
  export interface AuthProvider {
3
3
  login(page: Page): Promise<void>;
4
4
  }
5
+ export interface ApiAuthProvider {
6
+ getToken(request: any): Promise<string>;
7
+ }
5
8
  export interface AuthStorageConfig {
6
9
  enabled: boolean;
7
10
  validityMinutes: number;
@@ -0,0 +1,20 @@
1
+ import { ApiAuthProvider, AuthStorageConfig } from "../../types/auth";
2
+ /**
3
+ * Manages API token authentication with optional token caching.
4
+ *
5
+ * Flow:
6
+ * 1. If storage disabled → call getToken() directly, no caching
7
+ * 2. If valid cached token exists → return it, skip login call
8
+ * 3. If no valid token → call getToken(), save to file, return token
9
+ *
10
+ * Storage format: { token: "...", savedAt: 1234567890 }
11
+ *
12
+ * @returns JWT token string ready to use in Authorization header
13
+ */
14
+ export declare function initApiAuthSession(request: any, authStorage: AuthStorageConfig | undefined, creds: {
15
+ username: string;
16
+ password: string;
17
+ }, providerRegistry: Record<string, new (creds: {
18
+ username: string;
19
+ password: string;
20
+ }) => ApiAuthProvider>): Promise<string>;
@@ -0,0 +1,49 @@
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.initApiAuthSession = initApiAuthSession;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const storagePath_1 = require("./storagePath");
9
+ const validateStore_1 = require("./validateStore");
10
+ const resolveProvider_1 = require("./resolveProvider");
11
+ /**
12
+ * Manages API token authentication with optional token caching.
13
+ *
14
+ * Flow:
15
+ * 1. If storage disabled → call getToken() directly, no caching
16
+ * 2. If valid cached token exists → return it, skip login call
17
+ * 3. If no valid token → call getToken(), save to file, return token
18
+ *
19
+ * Storage format: { token: "...", savedAt: 1234567890 }
20
+ *
21
+ * @returns JWT token string ready to use in Authorization header
22
+ */
23
+ async function initApiAuthSession(request, authStorage, creds, providerRegistry) {
24
+ const ProviderClass = (0, resolveProvider_1.resolveProvider)(authStorage?.provider ?? "default", providerRegistry);
25
+ const authProvider = new ProviderClass(creds);
26
+ // ── Storage disabled → get token directly ────────────────────
27
+ if (!authStorage?.enabled) {
28
+ console.log("[Framework] API auth storage disabled — performing direct login");
29
+ return authProvider.getToken(request);
30
+ }
31
+ const { validityMinutes, provider } = authStorage;
32
+ const envName = process.env.TEST_ENV || "default";
33
+ const storagePath = (0, storagePath_1.getStoragePath)(provider, envName, creds.username);
34
+ // ── Valid cached token exists → return it ────────────────────
35
+ if ((0, validateStore_1.isAuthStoreValid)(storagePath, validityMinutes)) {
36
+ const saved = JSON.parse(fs_1.default.readFileSync(storagePath, "utf-8"));
37
+ console.log(`[Framework] ✅ Using cached API token for: ${creds.username}`);
38
+ return saved.token;
39
+ }
40
+ // ── No valid token → fresh login + save ──────────────────────
41
+ console.log("[Framework] Performing fresh API login...");
42
+ const token = await authProvider.getToken(request);
43
+ fs_1.default.writeFileSync(storagePath, JSON.stringify({
44
+ token,
45
+ savedAt: Date.now(),
46
+ }, null, 2));
47
+ console.log(`[Framework] New API token cached: ${storagePath}`);
48
+ return token;
49
+ }
@@ -1,5 +1,15 @@
1
1
  import { Page } from "@playwright/test";
2
2
  import { AuthProvider, AuthStorageConfig } from "../../types/auth";
3
+ /**
4
+ * Manages UI authentication with optional session storage.
5
+ *
6
+ * Flow:
7
+ * 1. If storage disabled → login directly, no caching
8
+ * 2. If valid stored session exists → restore it, skip login
9
+ * 3. If no valid session → perform fresh login, save session
10
+ *
11
+ * Supports both localStorage token apps (Nexus) and cookie-based apps.
12
+ */
3
13
  export declare function initAuthSession(page: Page, authStorage: AuthStorageConfig | undefined, creds: {
4
14
  username: string;
5
15
  password: string;
@@ -1,41 +1,62 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.initAuthSession = initAuthSession;
7
+ const fs_1 = __importDefault(require("fs"));
4
8
  const storagePath_1 = require("./storagePath");
5
9
  const validateStore_1 = require("./validateStore");
10
+ const resolveProvider_1 = require("./resolveProvider");
11
+ const restoreSession_1 = require("./restoreSession");
12
+ /**
13
+ * Manages UI authentication with optional session storage.
14
+ *
15
+ * Flow:
16
+ * 1. If storage disabled → login directly, no caching
17
+ * 2. If valid stored session exists → restore it, skip login
18
+ * 3. If no valid session → perform fresh login, save session
19
+ *
20
+ * Supports both localStorage token apps (Nexus) and cookie-based apps.
21
+ */
6
22
  async function initAuthSession(page, authStorage, creds, providerRegistry) {
23
+ const ProviderClass = (0, resolveProvider_1.resolveProvider)(authStorage?.provider ?? "default", providerRegistry);
24
+ const authProvider = new ProviderClass(creds);
25
+ // ── Storage disabled → login directly ────────────────────────
7
26
  if (!authStorage?.enabled) {
8
- console.log("[Framework] Auth storage disabled");
9
- const ProviderClass = providerRegistry[authStorage?.provider ?? "OrangeHRMLogin"];
10
- const authProvider = new ProviderClass(creds);
27
+ console.log("[Framework] Auth storage disabled — performing direct login");
11
28
  await authProvider.login(page);
12
29
  return;
13
30
  }
14
31
  const { validityMinutes, provider } = authStorage;
15
32
  const envName = process.env.TEST_ENV || "default";
16
33
  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;
34
+ // ── Valid session exists → restore it ────────────────────────
22
35
  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
- }
36
+ console.log(`[Framework] Restoring valid auth store: ${storagePath}`);
37
+ await (0, restoreSession_1.restoreSession)(page, storagePath);
38
+ console.log(`[Framework] ✅ Session restored from storage`);
39
+ return;
34
40
  }
35
- if (needsLogin) {
36
- console.log("[Framework] Performing fresh login...");
37
- await authProvider.login(page);
41
+ // ── No valid session → fresh login + save ────────────────────
42
+ console.log("[Framework] Performing fresh login...");
43
+ await authProvider.login(page);
44
+ // Save token + user from localStorage if present (token-based apps)
45
+ const saved = await page.evaluate(() => ({
46
+ token: localStorage.getItem("token"),
47
+ user: localStorage.getItem("user"),
48
+ }));
49
+ if (saved.token) {
50
+ // Token-based app — save token + user
51
+ fs_1.default.writeFileSync(storagePath, JSON.stringify({
52
+ token: saved.token,
53
+ user: saved.user ? JSON.parse(saved.user) : null,
54
+ savedAt: Date.now(),
55
+ }, null, 2));
56
+ }
57
+ else {
58
+ // Cookie-based app — save full Playwright storageState
38
59
  await page.context().storageState({ path: storagePath });
39
- console.log(`[Framework] New auth store created: ${storagePath}`);
40
60
  }
61
+ console.log(`[Framework] New auth store created: ${storagePath}`);
41
62
  }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Looks up a provider class from the registry by name.
3
+ * Throws a clear error if the provider is not registered.
4
+ *
5
+ * Used by both initAuthSession (UI) and initApiAuthSession (API).
6
+ */
7
+ export declare function resolveProvider<T>(providerName: string, providerRegistry: Record<string, new (creds: {
8
+ username: string;
9
+ password: string;
10
+ }) => T>): new (creds: {
11
+ username: string;
12
+ password: string;
13
+ }) => T;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveProvider = resolveProvider;
4
+ /**
5
+ * Looks up a provider class from the registry by name.
6
+ * Throws a clear error if the provider is not registered.
7
+ *
8
+ * Used by both initAuthSession (UI) and initApiAuthSession (API).
9
+ */
10
+ function resolveProvider(providerName, providerRegistry) {
11
+ const ProviderClass = providerRegistry[providerName];
12
+ if (!ProviderClass) {
13
+ throw new Error(`[Framework] Unknown auth provider: "${providerName}"\n` +
14
+ ` Registered providers: ${Object.keys(providerRegistry).join(", ")}`);
15
+ }
16
+ return ProviderClass;
17
+ }
@@ -0,0 +1,14 @@
1
+ import { Page } from "@playwright/test";
2
+ /**
3
+ * Restores a saved auth session into the browser page.
4
+ *
5
+ * Supports two formats:
6
+ *
7
+ * 1. localStorage token format: { token, user }
8
+ * Injects token and user into localStorage via addInitScript
9
+ * so they are available before the page loads.
10
+ *
11
+ * 2. Playwright storageState (cookies/origins):
12
+ * Restores cookies via addCookies — for cookie-based apps.
13
+ */
14
+ export declare function restoreSession(page: Page, storagePath: string): Promise<void>;
@@ -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.restoreSession = restoreSession;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ /**
9
+ * Restores a saved auth session into the browser page.
10
+ *
11
+ * Supports two formats:
12
+ *
13
+ * 1. localStorage token format: { token, user }
14
+ * Injects token and user into localStorage via addInitScript
15
+ * so they are available before the page loads.
16
+ *
17
+ * 2. Playwright storageState (cookies/origins):
18
+ * Restores cookies via addCookies — for cookie-based apps.
19
+ */
20
+ async function restoreSession(page, storagePath) {
21
+ const raw = fs_1.default.readFileSync(storagePath, "utf-8").trim();
22
+ const state = JSON.parse(raw);
23
+ // ── localStorage token format (Nexus and token-based apps) ───
24
+ if (state.token) {
25
+ await page.addInitScript((saved) => {
26
+ localStorage.setItem("token", saved.token);
27
+ if (saved.user)
28
+ localStorage.setItem("user", JSON.stringify(saved.user));
29
+ }, { token: state.token, user: state.user ?? null });
30
+ return;
31
+ }
32
+ // ── Playwright cookie-based storageState ─────────────────────
33
+ if (state.cookies?.length > 0) {
34
+ await page.context().addCookies(state.cookies);
35
+ }
36
+ }
@@ -1 +1,7 @@
1
+ /**
2
+ * Builds the file path for storing auth state.
3
+ * Creates the storage directory if it doesn't exist.
4
+ *
5
+ * Format: storage/{provider}-{env}-{username}-auth.json
6
+ */
1
7
  export declare function getStoragePath(provider: string, envName: string, username: string): string;
@@ -6,6 +6,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.getStoragePath = getStoragePath;
7
7
  const fs_1 = __importDefault(require("fs"));
8
8
  const path_1 = __importDefault(require("path"));
9
+ /**
10
+ * Builds the file path for storing auth state.
11
+ * Creates the storage directory if it doesn't exist.
12
+ *
13
+ * Format: storage/{provider}-{env}-{username}-auth.json
14
+ */
9
15
  function getStoragePath(provider, envName, username) {
10
16
  const storageDir = path_1.default.resolve("storage");
11
17
  if (!fs_1.default.existsSync(storageDir))
@@ -1 +1,14 @@
1
+ /**
2
+ * Checks if a stored auth file is valid and not expired.
3
+ *
4
+ * Supports two storage formats:
5
+ *
6
+ * 1. UI / cookie-based (Playwright storageState):
7
+ * { cookies: [...], origins: [...] }
8
+ * Valid if cookies or origins are non-empty and file age < validityMinutes
9
+ *
10
+ * 2. API / token-based:
11
+ * { token: "...", savedAt: 1234567890 }
12
+ * Valid if token exists and (now - savedAt) < validityMinutes
13
+ */
1
14
  export declare function isAuthStoreValid(storagePath: string, validityMinutes: number): boolean;
@@ -5,6 +5,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.isAuthStoreValid = isAuthStoreValid;
7
7
  const fs_1 = __importDefault(require("fs"));
8
+ /**
9
+ * Checks if a stored auth file is valid and not expired.
10
+ *
11
+ * Supports two storage formats:
12
+ *
13
+ * 1. UI / cookie-based (Playwright storageState):
14
+ * { cookies: [...], origins: [...] }
15
+ * Valid if cookies or origins are non-empty and file age < validityMinutes
16
+ *
17
+ * 2. API / token-based:
18
+ * { token: "...", savedAt: 1234567890 }
19
+ * Valid if token exists and (now - savedAt) < validityMinutes
20
+ */
8
21
  function isAuthStoreValid(storagePath, validityMinutes) {
9
22
  if (!fs_1.default.existsSync(storagePath))
10
23
  return false;
@@ -13,9 +26,19 @@ function isAuthStoreValid(storagePath, validityMinutes) {
13
26
  return false;
14
27
  try {
15
28
  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;
29
+ // ── API token format ──────────────────────────────────────
30
+ if (state.token && state.savedAt) {
31
+ const ageMinutes = (Date.now() - state.savedAt) / 60000;
32
+ return ageMinutes < validityMinutes;
33
+ }
34
+ // ── UI / Playwright storageState format ───────────────────
35
+ if (state.cookies !== undefined || state.origins !== undefined) {
36
+ const hasContent = (state.cookies?.length > 0) || (state.origins?.length > 0);
37
+ const stats = fs_1.default.statSync(storagePath);
38
+ const ageMinutes = (Date.now() - stats.mtimeMs) / 60000;
39
+ return hasContent && ageMinutes < validityMinutes;
40
+ }
41
+ return false;
19
42
  }
20
43
  catch {
21
44
  return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "simple-playwright-framework",
3
- "version": "0.0.17",
3
+ "version": "0.0.18",
4
4
  "description": "A modular Playwright framework with fixtures, loaders, and demo scaffolding.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",