@networkpro/web 1.25.10 → 1.25.11

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/CHANGELOG.md CHANGED
@@ -22,6 +22,27 @@ This project attempts to follow [Keep a Changelog](https://keepachangelog.com/en
22
22
 
23
23
  ---
24
24
 
25
+ ## [1.25.11] - 2025-11-12
26
+
27
+ ### Added
28
+
29
+ - `gotoDesktop(page, path)` and `gotoMobile(page, path)` helper functions to streamline viewport + navigation setup.
30
+ - `clickAndWaitForNavigation(page, locator, options)` utility for safe SPA or full-page navigation detection with optional URL pattern matching.
31
+ - `DEBUG_LOGS` flag in `helpers.js` to allow toggling of console logs for test diagnostics.
32
+ - Navigation debug logs to `getVisibleNav()` to indicate which navigation region was detected (when debugging is enabled).
33
+
34
+ ### Changed
35
+
36
+ - Refactored all E2E tests to use `gotoDesktop()` and `gotoMobile()` for consistency and DRY principles.
37
+ - Replaced brittle direct `waitForNavigation()` usages with `clickAndWaitForNavigation()` helper.
38
+ - Updated mobile and desktop tests to improve consistency across specs and improve visibility assertions.
39
+
40
+ ### Removed
41
+
42
+ - Legacy direct `setViewportSize()` and `page.goto()` calls from individual test blocks (now handled via `goto*()` helpers).
43
+
44
+ ---
45
+
25
46
  ## [1.25.10] - 2025-11-12
26
47
 
27
48
  ### Changed
@@ -1855,7 +1876,8 @@ This enables analytics filtering and CSP hardening for the audit environment.
1855
1876
 
1856
1877
  <!-- Link references -->
1857
1878
 
1858
- [Unreleased]: https://github.com/netwk-pro/netwk-pro.github.io/compare/v1.25.10...HEAD
1879
+ [Unreleased]: https://github.com/netwk-pro/netwk-pro.github.io/compare/v1.25.11...HEAD
1880
+ [1.25.11]: https://github.com/netwk-pro/netwk-pro.github.io/releases/tag/v1.25.11
1859
1881
  [1.25.10]: https://github.com/netwk-pro/netwk-pro.github.io/releases/tag/v1.25.10
1860
1882
  [1.25.9]: https://github.com/netwk-pro/netwk-pro.github.io/releases/tag/v1.25.9
1861
1883
  [1.25.8]: https://github.com/netwk-pro/netwk-pro.github.io/releases/tag/v1.25.8
package/cspell.json CHANGED
@@ -14,6 +14,7 @@
14
14
  "CCPA",
15
15
  "CPRA",
16
16
  "cryptomator",
17
+ "domcontentloaded",
17
18
  "dont",
18
19
  "elif",
19
20
  "Embedder",
@@ -28,6 +29,7 @@
28
29
  "homescreen",
29
30
  "HREFTOP",
30
31
  "HSTS",
32
+ "interactable",
31
33
  "Izzy",
32
34
  "Keybase",
33
35
  "keypair",
@@ -40,6 +42,7 @@
40
42
  "lightningcss",
41
43
  "linksheet",
42
44
  "linktree",
45
+ "LOCALAPPDATA",
43
46
  "lsheet",
44
47
  "Mailvelope",
45
48
  "Maricopa",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@networkpro/web",
3
3
  "private": false,
4
- "version": "1.25.10",
4
+ "version": "1.25.11",
5
5
  "description": "Locking Down Networks, Unlocking Confidence™ | Security, Networking, Privacy — Network Pro Strategies",
6
6
  "keywords": [
7
7
  "advisory",
@@ -54,6 +54,7 @@
54
54
  "verify": "npm run checkout",
55
55
  "delete": "rm -rf .svelte-kit node_modules package-lock.json",
56
56
  "clean": "npm run delete && npm cache clean --force && npm install",
57
+ "repair": "npm run delete && npx playwright uninstall && rm -rf %LOCALAPPDATA%/ms-playwright && npm install && npx playwright install",
57
58
  "upgrade": "ncu -u --format group --color",
58
59
  "check:updates": "ncu --format group --color",
59
60
  "test": "npm run test:all",
@@ -11,15 +11,16 @@ This file is part of Network Pro.
11
11
  * @description Runs Playwright E2E tests with desktop and root route assertions.
12
12
  * @module tests/e2e
13
13
  * @author Scott Lopez
14
- * @updated 2025-10-21
14
+ * @updated 2025-11-12
15
15
  */
16
16
 
17
17
  import { expect, test } from '@playwright/test';
18
18
  import {
19
+ clickAndWaitForNavigation,
19
20
  getFooter,
20
21
  getVisibleNav,
21
- setDesktopView,
22
- setMobileView,
22
+ gotoDesktop,
23
+ gotoMobile,
23
24
  } from './shared/helpers.js';
24
25
 
25
26
  // Root route should display nav bar and about link
@@ -29,9 +30,7 @@ test.describe('Desktop Tests', () => {
29
30
  test("should display the navigation bar and 'about' link", async ({
30
31
  page,
31
32
  }) => {
32
- await setDesktopView(page);
33
- await page.goto('/');
34
- await page.waitForLoadState('domcontentloaded', { timeout: 60000 });
33
+ await gotoDesktop(page);
35
34
 
36
35
  const nav = await getVisibleNav(page);
37
36
 
@@ -42,9 +41,7 @@ test.describe('Desktop Tests', () => {
42
41
 
43
42
  // Root route should display the footer properly
44
43
  test('should display the footer correctly', async ({ page }) => {
45
- await setDesktopView(page);
46
- await page.goto('/');
47
- await page.waitForLoadState('domcontentloaded', { timeout: 60000 });
44
+ await gotoDesktop(page);
48
45
 
49
46
  const footer = getFooter(page);
50
47
  await expect(footer).toBeVisible();
@@ -54,9 +51,7 @@ test.describe('Desktop Tests', () => {
54
51
  test("should render the 'about' page with footer visible", async ({
55
52
  page,
56
53
  }) => {
57
- await setDesktopView(page);
58
- await page.goto('/about');
59
- await page.waitForLoadState('domcontentloaded', { timeout: 60000 });
54
+ await gotoDesktop(page, '/about');
60
55
 
61
56
  const footer = getFooter(page);
62
57
  await expect(footer).toBeVisible();
@@ -64,20 +59,16 @@ test.describe('Desktop Tests', () => {
64
59
 
65
60
  // Root route should have a clickable "about" link
66
61
  test("should ensure the 'about' link is clickable", async ({ page }) => {
67
- await setDesktopView(page);
68
- await page.goto('/');
69
- await page.waitForLoadState('domcontentloaded', { timeout: 60000 });
62
+ await gotoDesktop(page);
70
63
 
71
64
  const nav = await getVisibleNav(page);
72
65
  const aboutLink = nav.getByRole('link', { name: 'about' });
73
- await expect(aboutLink).toBeVisible();
74
- await aboutLink.click();
75
66
 
76
- // safer wait pattern with load state
77
- await Promise.all([
78
- page.waitForLoadState('load'),
79
- page.waitForURL('**/about', { timeout: 60000 }),
80
- ]);
67
+ await clickAndWaitForNavigation(page, aboutLink, {
68
+ urlPattern: /\/about/,
69
+ timeout: 60000,
70
+ });
71
+
81
72
  await expect(page).toHaveURL(/\/about/);
82
73
  });
83
74
  }); // End Desktop Tests
@@ -85,13 +76,10 @@ test.describe('Desktop Tests', () => {
85
76
  // Root route should display headings properly on mobile
86
77
  test.describe('Mobile Tests', () => {
87
78
  test('should display main content correctly on mobile', async ({ page }) => {
88
- await setMobileView(page);
89
- await page.goto('/');
90
- await page.waitForLoadState('domcontentloaded', { timeout: 60000 });
79
+ await gotoMobile(page);
91
80
 
92
- const mainHeading = page.locator('h1, h2');
93
- await expect(mainHeading).toBeVisible();
81
+ // Ensure main headline is present on mobile
82
+ const h2 = page.locator('h2.index-title2');
83
+ await expect(h2).toContainText(/security/i);
94
84
  });
95
85
  });
96
-
97
- // cspell:ignore domcontentloaded
@@ -11,7 +11,7 @@ This file is part of Network Pro.
11
11
  * @description Runs Playwright E2E tests with mobile assertions.
12
12
  * @module tests/e2e
13
13
  * @author Scott Lopez
14
- * @updated 2025-10-21
14
+ * @updated 2025-11-12
15
15
  */
16
16
 
17
17
  import { expect, test } from '@playwright/test';
@@ -19,7 +19,7 @@ import {
19
19
  clickAndWaitForNavigation,
20
20
  getFooter,
21
21
  getVisibleNav,
22
- setMobileView,
22
+ gotoMobile,
23
23
  } from './shared/helpers.js';
24
24
 
25
25
  // ---------------------------------------------------------------------------
@@ -27,21 +27,20 @@ import {
27
27
  // ---------------------------------------------------------------------------
28
28
 
29
29
  test.describe('Mobile Tests', () => {
30
- // Increase timeout for all mobile tests
31
30
  test.setTimeout(90_000);
32
31
 
32
+ test.beforeEach(({ browserName }) => {
33
+ if (browserName === 'webkit')
34
+ test.skip('Skipping WebKit: manual validation only');
35
+ });
36
+
33
37
  // -------------------------------------------------------------------------
34
38
  // 🧱 Test 1: Main description text
35
39
  // -------------------------------------------------------------------------
36
40
  test('should display the main description text on mobile', async ({
37
41
  page,
38
- browserName,
39
42
  }) => {
40
- if (browserName === 'webkit') test.skip();
41
-
42
- await setMobileView(page);
43
- await page.goto('/');
44
- await page.waitForLoadState('domcontentloaded', { timeout: 60_000 });
43
+ await gotoMobile(page);
45
44
 
46
45
  const description = page.locator(
47
46
  'div.index-title1:has-text("Locking Down Networks")',
@@ -52,18 +51,11 @@ test.describe('Mobile Tests', () => {
52
51
  // -------------------------------------------------------------------------
53
52
  // 🧱 Test 2: Main content
54
53
  // -------------------------------------------------------------------------
55
- test('should display main content correctly on mobile', async ({
56
- page,
57
- browserName,
58
- }) => {
59
- if (browserName === 'webkit') test.skip();
60
-
61
- await setMobileView(page);
62
- await page.goto('/');
63
- await page.waitForLoadState('domcontentloaded', { timeout: 60_000 });
54
+ test('should display main content correctly on mobile', async ({ page }) => {
55
+ await gotoMobile(page);
64
56
 
65
- const mainHeading = page.locator('h1, h2');
66
- await expect(mainHeading).toBeVisible();
57
+ const mainHeading = page.locator('h2.index-title2');
58
+ await expect(mainHeading).toContainText(/security/i);
67
59
  });
68
60
 
69
61
  // -------------------------------------------------------------------------
@@ -71,41 +63,27 @@ test.describe('Mobile Tests', () => {
71
63
  // -------------------------------------------------------------------------
72
64
  test("should ensure the 'about' link is clickable on mobile", async ({
73
65
  page,
74
- browserName,
75
66
  }) => {
76
- if (browserName === 'webkit') test.skip();
77
-
78
- await setMobileView(page);
79
- await page.goto('/', { waitUntil: 'networkidle' });
67
+ await gotoMobile(page);
80
68
 
81
69
  const nav = await getVisibleNav(page);
82
-
83
- // Ensure the link exists before interacting
84
- await page.waitForSelector('a[href="/about"]', { timeout: 10_000 });
85
70
  const aboutLink = nav.getByRole('link', { name: 'about' });
86
71
 
87
- // Use the safe navigation helper
88
72
  await clickAndWaitForNavigation(page, aboutLink, {
89
73
  urlPattern: /\/about/,
90
74
  timeout: 60_000,
91
75
  });
76
+
77
+ await expect(page).toHaveURL(/\/about/);
92
78
  });
93
79
 
94
80
  // -------------------------------------------------------------------------
95
81
  // 🧱 Test 4: Footer presence on /about
96
82
  // -------------------------------------------------------------------------
97
- test('should display the footer on /about (mobile)', async ({
98
- page,
99
- browserName,
100
- }) => {
101
- if (browserName === 'webkit') test.skip();
102
-
103
- await setMobileView(page);
104
- await page.goto('/about');
83
+ test('should display the footer on /about (mobile)', async ({ page }) => {
84
+ await gotoMobile(page, '/about');
105
85
 
106
86
  const footer = getFooter(page);
107
87
  await expect(footer).toBeVisible();
108
88
  });
109
89
  });
110
-
111
- // cspell:ignore domcontentloaded networkidle
@@ -11,19 +11,19 @@ This file is part of Network Pro.
11
11
  * @description Stores commonly used functions for importing into E2E tests.
12
12
  * @module tests/e2e/shared
13
13
  * @author Scott Lopez
14
- * @updated 2025-10-29
14
+ * @updated 2025-11-12
15
15
  */
16
16
 
17
- import { expect } from '@playwright/test';
17
+ const DEBUG_LOGS = false; // set to true to enable console logs
18
18
 
19
19
  /**
20
- * @param {import('@playwright/test').Page} page - The Playwright page object.
20
+ * @param {import('@playwright/test').Page} page
21
21
  * @returns {Promise<void>}
22
- * @description Sets standard desktop viewport and waits for animations.
22
+ * @description Sets desktop viewport and allows layout to settle.
23
23
  */
24
24
  export async function setDesktopView(page) {
25
25
  await page.setViewportSize({ width: 1280, height: 720 });
26
- await page.waitForTimeout(1500);
26
+ await page.waitForTimeout(1500); // Known-stable stabilization delay
27
27
  }
28
28
 
29
29
  /**
@@ -36,18 +36,47 @@ export async function setMobileView(page) {
36
36
  }
37
37
 
38
38
  /**
39
- * @param {import('@playwright/test').Page} page - The Playwright page object.
40
- * @returns {Promise<import('@playwright/test').Locator>} - A visible navigation locator.
41
- * @throws {Error} If no visible navigation is found.
39
+ * Navigate with desktop viewport + initial DOM load.
40
+ * @param {import('@playwright/test').Page} page
41
+ * @param {string} path
42
+ */
43
+ export async function gotoDesktop(page, path = '/') {
44
+ await setDesktopView(page);
45
+ await page.goto(path, { waitUntil: 'domcontentloaded', timeout: 60000 });
46
+ }
47
+
48
+ /**
49
+ * Navigate with mobile viewport + initial DOM load.
50
+ * @param {import('@playwright/test').Page} page
51
+ * @param {string} path
52
+ */
53
+ export async function gotoMobile(page, path = '/') {
54
+ await setMobileView(page);
55
+ await page.goto(path, { waitUntil: 'domcontentloaded', timeout: 60000 });
56
+ }
57
+
58
+ /**
59
+ * Returns the visible navigation region.
60
+ * @param {import('@playwright/test').Page} page
61
+ * @returns {Promise<import('@playwright/test').Locator>}
42
62
  */
43
63
  export async function getVisibleNav(page) {
44
64
  const navHome = page.getByRole('navigation', { name: 'Homepage navigation' });
45
65
  const navMain = page.getByRole('navigation', { name: 'Main navigation' });
46
66
 
47
- if (await navHome.isVisible()) return navHome;
48
- if (await navMain.isVisible()) return navMain;
67
+ const timeout = 5000;
68
+
69
+ if (await navHome.isVisible({ timeout }).catch(() => false)) {
70
+ if (DEBUG_LOGS) console.log('✅ Detected visible nav: Homepage navigation');
71
+ return navHome;
72
+ }
49
73
 
50
- throw new Error('No visible navigation element found.');
74
+ if (await navMain.isVisible({ timeout }).catch(() => false)) {
75
+ if (DEBUG_LOGS) console.log('✅ Detected visible nav: Main navigation');
76
+ return navMain;
77
+ }
78
+
79
+ throw new Error('❌ No visible navigation element found within timeout.');
51
80
  }
52
81
 
53
82
  /**
@@ -59,37 +88,28 @@ export function getFooter(page) {
59
88
  }
60
89
 
61
90
  /**
62
- * @function clickAndWaitForNavigation
63
- * @description Clicks a link or button and waits for navigation or URL change.
64
- * Works for both SPA (client-side) and full-page navigations.
91
+ * Click + wait for SPA or full navigation event.
65
92
  *
66
93
  * @param {import('@playwright/test').Page} page
67
94
  * @param {import('@playwright/test').Locator} locator
68
- * @param {object} [options]
69
- * @param {string|RegExp} [options.urlPattern] - URL or regex to match
70
- * @param {number} [options.timeout=60000] - Max wait time
95
+ * @param {{ urlPattern?: string | RegExp, timeout?: number }} [options]
71
96
  */
72
97
  export async function clickAndWaitForNavigation(page, locator, options = {}) {
73
98
  const { urlPattern = /\/.*/, timeout = 60000 } = options;
74
99
 
75
- // Ensure the element is ready for interaction
76
100
  await locator.scrollIntoViewIfNeeded();
77
- await locator.waitFor({ state: 'visible', timeout: 10000 });
78
- await locator.click({ trial: true });
101
+ await locator.waitFor({ state: 'visible', timeout: 60000 });
79
102
 
80
- // Capture current URL to detect SPA navigation
81
- const currentUrl = page.url();
103
+ const previousURL = page.url();
82
104
 
83
- // Handle both SPA transitions and full page reloads
84
- await Promise.all([
85
- page.waitForURL(urlPattern, { timeout }).catch(() => {}),
86
- page.waitForLoadState('load', { timeout }).catch(() => {}),
87
- locator.click(),
105
+ const [, newURL] = await Promise.all([
106
+ page.waitForURL(
107
+ (url) =>
108
+ url.toString() !== previousURL && urlPattern.test(url.toString()),
109
+ { timeout },
110
+ ),
111
+ locator.click().then(() => page.url()),
88
112
  ]);
89
113
 
90
- // Confirm the URL changed successfully
91
- await expect(page).toHaveURL(urlPattern, { timeout });
92
-
93
- // Optional: log navigation success (helps in CI)
94
- console.log(`✅ Navigation from ${currentUrl} → ${page.url()}`);
114
+ console.log(`✅ Navigation from ${previousURL} ${newURL}`);
95
115
  }