@networkpro/web 1.7.0 → 1.7.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.
package/README.md CHANGED
@@ -31,10 +31,14 @@ All infrastructure and data flows are designed with **maximum transparency, self
31
31
  ```bash
32
32
  .
33
33
  ├── .github/workflows/ # CI workflows and automation
34
- ├── .vscode/ # Recommended VS Code settings, extensions
34
+ ├── .vscode/
35
+ │ ├── customData.json # Custom CSS data for FontAwesome icons
36
+ │ ├── extensions.json # Recommended VSCodium / VS Code extensions
37
+ │ ├── extensions.jsonc # Commented version of extensions.json for reference
38
+ │ └── settings.json # User settings for VSCodium / VS Code
35
39
  ├── netlify-functions/
36
40
  │ └── cspReport.js # Serverless function to receive and log CSP violation reports
37
- ├── scripts/ # Utility scripts
41
+ ├── scripts/ # General utility scripts
38
42
  ├── src/
39
43
  │ ├── lib/ # Reusable components, styles, utilities
40
44
  │ ├── routes/ # SvelteKit routes (+page.svelte, +page.server.js)
@@ -43,13 +47,33 @@ All infrastructure and data flows are designed with **maximum transparency, self
43
47
  │ ├── app.html # SvelteKit entry HTML with CSP/meta/bootentry
44
48
  │ └── service-worker.js # Custom Service Worker
45
49
  ├── static/ # Static assets served at root
50
+ │ ├── manifest.json # Manifest file for PWA configuration
51
+ │ ├── robots.txt # Instructions for web robots
52
+ │ └── sitemap.xml # Sitemap for search engines
46
53
  ├── tests/
47
54
  │ ├── e2e/ # End-to-end Playwright tests
48
55
  │ └── unit/ # Vite unit tests
56
+ ├── _redirects # Netlify redirects
49
57
  ├── netlify.toml # Netlify configuration
50
58
  └── ...
51
59
  ```
52
60
 
61
+  
62
+
63
+ ### E2E Test Structure
64
+
65
+ End-to-end tests are located in `tests/e2e/` and organized by feature or route:
66
+
67
+ ```bash
68
+ tests/
69
+ ├── e2e/
70
+ │ ├── app.spec.js # Desktop and mobile route tests
71
+ │ ├── mobile.spec.js # Mobile-specific assertions
72
+ │ └── shared/
73
+ │ └── helpers.js # Shared test utilities (e.g., getFooter, setDesktopView, setMobileView)
74
+ └── ...
75
+ ```
76
+
53
77
  ---
54
78
 
55
79
  ## 🛠 Getting Started
@@ -180,7 +204,7 @@ Located at `src/hooks.server.js`, this file is responsible for injecting dynamic
180
204
  To re-enable nonce generation for inline scripts in the future:
181
205
 
182
206
  1. Uncomment the nonce generation and injection logic in `hooks.server.js`.
183
- 2. Add `nonce="**cspNonce**"` to inline `<script>` blocks in `app.html` or route templates.
207
+ 2. Add `nonce="__cspNonce__"` to inline `<script>` blocks in `app.html` or route templates.
184
208
 
185
209
  > 💡 The `[headers]` block in `netlify.toml` has been deprecated — all headers are now set dynamically from within SvelteKit.
186
210
 
@@ -244,6 +268,24 @@ This project uses a mix of automated performance, accessibility, and end-to-end
244
268
 
245
269
  &nbsp;
246
270
 
271
+ ### E2E Setup
272
+
273
+ Playwright is included in `devDependencies` and installed automatically with:
274
+
275
+ ```bash
276
+ npm install
277
+ ```
278
+
279
+ To install browser dependencies (required once):
280
+
281
+ ```bash
282
+ npx playwright install
283
+ ```
284
+
285
+ > This downloads the browser binaries (Chromium, Firefox, WebKit) used for testing. You only need to run this once per machine or after a fresh clone.
286
+
287
+ &nbsp;
288
+
247
289
  ### Running Tests
248
290
 
249
291
  Local testing via Vitest and Playwright:
@@ -254,9 +296,11 @@ npm run test:server # Run server-side unit tests with Vitest
254
296
  npm run test:all # Run full test suite
255
297
  npm run test:watch # Watch mode for client tests
256
298
  npm run test:coverage # Collect code coverage reports
257
- npm run test:e2e # Runs Playwright E2E tests
299
+ npm run test:e2e # Runs Playwright E2E tests (with one retry on failure)
258
300
  ```
259
301
 
302
+ > Playwright will retry failed tests once `(--retries=1)` to reduce false negatives from transient flakiness (network, render delay, etc.).
303
+
260
304
  Audit your app using Lighthouse:
261
305
 
262
306
  ```bash
@@ -399,15 +443,15 @@ The following CLI commands are available via `npm run <script>` or `pnpm run <sc
399
443
 
400
444
  <!-- markdownlint-enable MD024 -->
401
445
 
402
- | Script | Description |
403
- | --------------- | -------------------------------------------- |
404
- | `test` | Alias for `test:all` |
405
- | `test:all` | Run both client and server test suites |
406
- | `test:client` | Run client tests with Vitest |
407
- | `test:server` | Run server-side tests with Vitest |
408
- | `test:watch` | Watch mode for client tests |
409
- | `test:coverage` | Collect coverage from both client and server |
410
- | `test:e2e` | Run Playwright E2E tests |
446
+ | Script | Description |
447
+ | --------------- | ------------------------------------------------------ |
448
+ | `test` | Alias for `test:all` |
449
+ | `test:all` | Run both client and server test suites |
450
+ | `test:client` | Run client tests with Vitest |
451
+ | `test:server` | Run server-side tests with Vitest |
452
+ | `test:watch` | Watch mode for client tests |
453
+ | `test:coverage` | Collect coverage from both client and server |
454
+ | `test:e2e` | Runs E2E tests with up to 1 automatic retry on failure |
411
455
 
412
456
  ---
413
457
 
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "sideEffects": [
5
5
  "./.netlify/shims.js"
6
6
  ],
7
- "version": "1.7.0",
7
+ "version": "1.7.2",
8
8
  "description": "Locking Down Networks, Unlocking Confidence | Security, Networking, Privacy — Network Pro Strategies",
9
9
  "keywords": [
10
10
  "advisory",
@@ -60,7 +60,7 @@
60
60
  "test:server": "vitest --config vitest.config.server.js",
61
61
  "test:watch": "vitest --config vitest.config.client.js --watch",
62
62
  "test:coverage": "npm run test:client -- --run --coverage && npm run test:server -- --run --coverage",
63
- "test:e2e": "npx playwright test",
63
+ "test:e2e": "npx playwright test --retries=1",
64
64
  "lint": "eslint . --ext .mjs,.js,.svelte",
65
65
  "lint:fix": "eslint . --ext .mjs,.js,.svelte --fix",
66
66
  "lint:jsdoc": "eslint . --ext .js,.mjs,.svelte --max-warnings=0",
@@ -80,7 +80,7 @@
80
80
  "nodemailer": "^7.0.3",
81
81
  "posthog-js": "^1.248.1",
82
82
  "semver": "^7.7.2",
83
- "svelte": "5.33.6"
83
+ "svelte": "5.33.10"
84
84
  },
85
85
  "devDependencies": {
86
86
  "@eslint/compat": "^1.2.9",
@@ -106,10 +106,10 @@
106
106
  "markdownlint-cli2": "^0.18.1",
107
107
  "mdsvex": "^0.12.6",
108
108
  "playwright": "^1.52.0",
109
- "postcss": "^8.5.3",
109
+ "postcss": "^8.5.4",
110
110
  "prettier": "^3.5.3",
111
111
  "prettier-plugin-svelte": "^3.4.0",
112
- "stylelint": "^16.19.1",
112
+ "stylelint": "^16.20.0",
113
113
  "stylelint-config-html": "^1.1.0",
114
114
  "stylelint-config-recommended": "^16.0.0",
115
115
  "stylelint-order": "^7.0.0",
package/static/robots.txt CHANGED
@@ -1,9 +1,10 @@
1
1
  # robots.txt
2
2
  #
3
+ # Copyright © 2025 Network Pro Strategies (Network Pro™)
3
4
  # SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
4
5
  # This file is part of Network Pro
5
6
  #
6
- # www.robotstxt.org/
7
+ # www.robotstxt.org
7
8
 
8
9
  User-agent: *
9
10
 
@@ -6,8 +6,23 @@ SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
6
6
  This file is part of Network Pro.
7
7
  ========================================================================== */
8
8
 
9
- import { expect, test } from "@playwright/test";
9
+ /**
10
+ * @file app.spec.js
11
+ * @description Runs Playwright E2E tests with desktop and root route assertions.
12
+ * @module tests/e2e
13
+ * @author SunDevil311
14
+ * @updated 2025-05-29
15
+ */
10
16
 
17
+ import { expect, test } from "@playwright/test";
18
+ import {
19
+ getFooter,
20
+ getVisibleNav,
21
+ setDesktopView,
22
+ setMobileView,
23
+ } from "./shared/helpers.js";
24
+
25
+ // Root route should load successfully with the correct title
11
26
  test.describe("Desktop Tests", () => {
12
27
  test("should load successfully with the correct title", async ({
13
28
  page,
@@ -15,43 +30,52 @@ test.describe("Desktop Tests", () => {
15
30
  }) => {
16
31
  if (browserName === "webkit") test.skip();
17
32
 
18
- await page.setViewportSize({ width: 1280, height: 720 });
19
- await page.waitForTimeout(1500);
33
+ await setDesktopView(page);
20
34
  await page.goto("/");
21
35
  await page.waitForLoadState("load", { timeout: 60000 });
22
36
  await expect(page).toHaveTitle(/Locking Down Networks/);
23
37
  });
24
38
 
39
+ // Root route should display nav bar and about link
25
40
  test("should display the navigation bar and 'about' link", async ({
26
41
  page,
27
42
  }) => {
28
- await page.setViewportSize({ width: 1280, height: 720 });
29
- await page.waitForTimeout(1500);
43
+ await setDesktopView(page);
30
44
  await page.goto("/");
31
45
 
32
- const nav = page.getByRole("navigation", { name: "Homepage navigation" });
33
- await expect(nav).toBeVisible();
46
+ const nav = await getVisibleNav(page);
34
47
 
35
48
  const aboutLink = nav.getByRole("link", { name: "about" });
36
49
  await expect(aboutLink).toBeVisible();
37
50
  await expect(aboutLink).toHaveAttribute("href", "/about");
38
51
  });
39
52
 
53
+ // Root route should display the footer properly
40
54
  test("should display the footer correctly", async ({ page }) => {
41
- await page.setViewportSize({ width: 1280, height: 720 });
42
- await page.waitForTimeout(1500);
55
+ await setDesktopView(page);
43
56
  await page.goto("/");
44
57
 
45
58
  const footer = page.locator("footer");
46
59
  await expect(footer).toBeVisible();
47
60
  });
48
61
 
62
+ // About route should display the footer properly
63
+ test("should render the 'about' page with footer visible", async ({
64
+ page,
65
+ }) => {
66
+ await setDesktopView(page);
67
+ await page.goto("/about");
68
+
69
+ const footer = getFooter(page);
70
+ await expect(footer).toBeVisible();
71
+ });
72
+
73
+ // Root route should have a clickable "about" link
49
74
  test("should ensure the 'about' link is clickable", async ({ page }) => {
50
- await page.setViewportSize({ width: 1280, height: 720 });
75
+ await setDesktopView(page);
51
76
  await page.goto("/");
52
77
 
53
- const nav = page.getByRole("navigation", { name: "Homepage navigation" });
54
- await expect(nav).toBeVisible({ timeout: 60000 });
78
+ const nav = await getVisibleNav(page);
55
79
 
56
80
  const aboutLink = nav.getByRole("link", { name: "about" });
57
81
  await expect(aboutLink).toBeVisible({ timeout: 60000 });
@@ -62,6 +86,7 @@ test.describe("Desktop Tests", () => {
62
86
  });
63
87
  }); // End Desktop Tests
64
88
 
89
+ // Root route should load successfully with the correct title (mobile)
65
90
  test.describe("Mobile Tests", () => {
66
91
  test("should load successfully with the correct title on mobile", async ({
67
92
  page,
@@ -69,16 +94,15 @@ test.describe("Mobile Tests", () => {
69
94
  }) => {
70
95
  if (browserName === "webkit") test.skip();
71
96
 
72
- await page.setViewportSize({ width: 375, height: 667 });
73
- await page.waitForTimeout(1500);
97
+ await setMobileView(page);
74
98
  await page.goto("/");
75
99
  await page.waitForLoadState("load", { timeout: 60000 });
76
100
  await expect(page).toHaveTitle(/Locking Down Networks/);
77
101
  });
78
102
 
103
+ // Root route should display headings properly on mobile
79
104
  test("should display main content correctly on mobile", async ({ page }) => {
80
- await page.setViewportSize({ width: 375, height: 667 });
81
- await page.waitForTimeout(1500);
105
+ await setMobileView(page);
82
106
  await page.goto("/");
83
107
 
84
108
  const mainHeading = page.locator("h1, h2");
@@ -6,33 +6,29 @@ SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
6
6
  This file is part of Network Pro.
7
7
  ========================================================================== */
8
8
 
9
+ /**
10
+ * @file mobile.spec.js
11
+ * @description Runs Playwright E2E tests with mobile assertions.
12
+ * @module tests/e2e
13
+ * @author SunDevil311
14
+ * @updated 2025-05-29
15
+ */
16
+
9
17
  import { expect, test } from "@playwright/test";
18
+ import { getFooter, getVisibleNav, setMobileView } from "./shared/helpers.js";
10
19
 
20
+ // Mobile viewport smoke tests for the root route
11
21
  test.describe("Mobile Tests", () => {
12
- // Skip WebKit for mobile tests if it's problematic
13
22
  test("should display the main description text on mobile", async ({
14
23
  page,
15
24
  browserName,
16
25
  }) => {
17
- if (browserName === "webkit") {
18
- test.skip(); // Skip WebKit if it's problematic
19
- }
20
-
21
- await page.setViewportSize({ width: 375, height: 667 }); // Mobile size (e.g., iPhone 6)
22
-
23
- // Add a small timeout before navigating to the page
24
- await page.waitForTimeout(1500); // Wait for 1.5 seconds
26
+ if (browserName === "webkit") test.skip();
25
27
 
28
+ await setMobileView(page);
26
29
  await page.goto("/");
27
-
28
- // Wait for the page to load and for the title element to be available
29
30
  await page.waitForLoadState("domcontentloaded", { timeout: 60000 });
30
- await page.waitForSelector(
31
- 'div.index-title1:has-text("Locking Down Networks")',
32
- { timeout: 60000 },
33
- );
34
31
 
35
- // Assert that the correct text is found inside the <div>
36
32
  const description = page.locator(
37
33
  'div.index-title1:has-text("Locking Down Networks")',
38
34
  );
@@ -43,21 +39,12 @@ test.describe("Mobile Tests", () => {
43
39
  page,
44
40
  browserName,
45
41
  }) => {
46
- if (browserName === "webkit") {
47
- test.skip(); // Skip WebKit if it's problematic
48
- }
49
-
50
- await page.setViewportSize({ width: 375, height: 667 }); // Mobile size
51
-
52
- // Add a small timeout before navigating to the page
53
- await page.waitForTimeout(1500); // Wait for 1.5 seconds
42
+ if (browserName === "webkit") test.skip();
54
43
 
44
+ await setMobileView(page);
55
45
  await page.goto("/");
56
-
57
- // Wait for the page to load
58
46
  await page.waitForLoadState("domcontentloaded", { timeout: 60000 });
59
47
 
60
- // Check that the main heading is visible on mobile
61
48
  const mainHeading = page.locator("h1, h2");
62
49
  await expect(mainHeading).toBeVisible();
63
50
  });
@@ -66,28 +53,31 @@ test.describe("Mobile Tests", () => {
66
53
  page,
67
54
  browserName,
68
55
  }) => {
69
- if (browserName === "webkit") {
70
- test.skip(); // Skip WebKit if it's problematic
71
- }
72
-
73
- await page.setViewportSize({ width: 375, height: 667 }); // Mobile size
74
-
75
- // Add a small timeout before navigating to the page
76
- await page.waitForTimeout(1500); // Wait for 1.5 seconds
56
+ if (browserName === "webkit") test.skip();
77
57
 
58
+ await setMobileView(page);
78
59
  await page.goto("/");
79
60
 
80
- // Wait for the page to load
81
- await page.waitForLoadState("domcontentloaded", { timeout: 60000 });
61
+ const nav = await getVisibleNav(page);
62
+ const aboutLink = nav.getByRole("link", { name: "about" });
63
+ await expect(aboutLink).toBeVisible({ timeout: 60000 });
82
64
 
83
- // Ensure the "about" link is visible and clickable
84
- const aboutLink = page.locator("a", { hasText: "about" });
85
- await expect(aboutLink).toBeVisible();
86
65
  await aboutLink.click();
87
-
88
- // Assert that it navigates to the correct route
89
66
  await expect(page).toHaveURL(/\/about/);
90
67
  });
68
+
69
+ test("should display the footer on /about (mobile)", async ({
70
+ page,
71
+ browserName,
72
+ }) => {
73
+ if (browserName === "webkit") test.skip();
74
+
75
+ await setMobileView(page);
76
+ await page.goto("/about");
77
+
78
+ const footer = getFooter(page);
79
+ await expect(footer).toBeVisible();
80
+ });
91
81
  });
92
82
 
93
83
  // cspell:ignore domcontentloaded
@@ -0,0 +1,57 @@
1
+ /* ==========================================================================
2
+ tests/e2e/shared/helpers.js
3
+
4
+ Copyright © 2025 Network Pro Strategies (Network Pro™)
5
+ SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
6
+ This file is part of Network Pro.
7
+ ========================================================================== */
8
+
9
+ /**
10
+ * @file helpers.js
11
+ * @description Stores commonly used functions for importing into E2E tests.
12
+ * @module tests/e2e/shared
13
+ * @author SunDevil311
14
+ * @updated 2025-05-29
15
+ */
16
+
17
+ /**
18
+ * @param {import('@playwright/test').Page} page - The Playwright page object.
19
+ * @returns {Promise<void>}
20
+ * @description Sets standard desktop viewport and waits for animations.
21
+ */
22
+ export async function setDesktopView(page) {
23
+ await page.setViewportSize({ width: 1280, height: 720 });
24
+ await page.waitForTimeout(1500);
25
+ }
26
+
27
+ /**
28
+ * @param {import('@playwright/test').Page} page
29
+ * @returns {Promise<void>}
30
+ */
31
+ export async function setMobileView(page) {
32
+ await page.setViewportSize({ width: 375, height: 667 });
33
+ await page.waitForTimeout(1500);
34
+ }
35
+
36
+ /**
37
+ * @param {import('@playwright/test').Page} page - The Playwright page object.
38
+ * @returns {Promise<import('@playwright/test').Locator>} - A visible navigation locator.
39
+ * @throws {Error} If no visible navigation is found.
40
+ */
41
+ export async function getVisibleNav(page) {
42
+ const navHome = page.getByRole("navigation", { name: "Homepage navigation" });
43
+ const navMain = page.getByRole("navigation", { name: "Main navigation" });
44
+
45
+ if (await navHome.isVisible()) return navHome;
46
+ if (await navMain.isVisible()) return navMain;
47
+
48
+ throw new Error("No visible navigation element found.");
49
+ }
50
+
51
+ /**
52
+ * @param {import('@playwright/test').Page} page
53
+ * @returns {import('@playwright/test').Locator}
54
+ */
55
+ export function getFooter(page) {
56
+ return page.locator("footer");
57
+ }