@networkpro/web 1.12.8 → 1.13.0

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 (104) hide show
  1. package/CHANGELOG.md +64 -1
  2. package/README.md +8 -8
  3. package/cspell.json +1 -0
  4. package/eslint.config.mjs +48 -48
  5. package/jsconfig.template.jsonc +3 -1
  6. package/netlify/edge-functions/csp-report.js +31 -31
  7. package/package.json +6 -6
  8. package/playwright.config.js +14 -14
  9. package/postcss.config.mjs +1 -1
  10. package/scripts/auditScripts.js +16 -16
  11. package/scripts/bundleCss.js +5 -5
  12. package/scripts/checkEnv.js +6 -6
  13. package/scripts/checkNode.js +10 -10
  14. package/scripts/checkVersions.js +6 -6
  15. package/scripts/flattenHeaders.js +13 -13
  16. package/scripts/generateTest.js +5 -5
  17. package/scripts/openReport.js +3 -3
  18. package/scripts/validateHeaders.js +13 -13
  19. package/src/hooks.client.ts +1 -1
  20. package/src/hooks.server.js +31 -31
  21. package/src/lib/components/Badges.svelte +9 -9
  22. package/src/lib/components/CodeBlock.svelte +13 -0
  23. package/src/lib/components/ContainerSection.svelte +1 -1
  24. package/src/lib/components/FullWidthSection.svelte +3 -3
  25. package/src/lib/components/LegalNav.svelte +6 -6
  26. package/src/lib/components/Logo.svelte +9 -9
  27. package/src/lib/components/MetaTags.svelte +3 -3
  28. package/src/lib/components/PWAInstallButton.svelte +4 -4
  29. package/src/lib/components/RedirectPage.svelte +4 -4
  30. package/src/lib/components/SocialMedia.svelte +16 -16
  31. package/src/lib/components/foss/FossItemContent.svelte +18 -18
  32. package/src/lib/components/layout/Footer.svelte +17 -17
  33. package/src/lib/components/layout/HeaderDefault.svelte +16 -16
  34. package/src/lib/components/layout/HeaderHome.svelte +14 -14
  35. package/src/lib/images.js +34 -34
  36. package/src/lib/index.js +15 -15
  37. package/src/lib/meta.js +29 -29
  38. package/src/lib/pages/AboutContent.svelte +24 -24
  39. package/src/lib/pages/FossContent.svelte +12 -12
  40. package/src/lib/pages/HomeContent.svelte +6 -6
  41. package/src/lib/pages/LicenseContent.svelte +38 -38
  42. package/src/lib/pages/PGPContent.svelte +61 -29
  43. package/src/lib/pages/PrivacyContent.svelte +39 -39
  44. package/src/lib/pages/PrivacyDashboard.svelte +12 -12
  45. package/src/lib/pages/TermsConditionsContent.svelte +28 -28
  46. package/src/lib/pages/TermsUseContent.svelte +26 -26
  47. package/src/lib/registerServiceWorker.js +25 -25
  48. package/src/lib/stores/posthog.js +13 -13
  49. package/src/lib/stores/trackingPreferences.js +19 -19
  50. package/src/lib/styles/css/default.css +5 -1
  51. package/src/lib/styles/global.min.css +1 -1
  52. package/src/lib/unregisterServiceWorker.js +1 -1
  53. package/src/lib/utils/purify.js +4 -4
  54. package/src/lib/utils/utm.js +2 -2
  55. package/src/routes/+error.svelte +4 -4
  56. package/src/routes/+layout.js +6 -6
  57. package/src/routes/+layout.svelte +29 -29
  58. package/src/routes/+page.server.js +2 -2
  59. package/src/routes/+page.svelte +9 -9
  60. package/src/routes/about/+page.server.js +2 -2
  61. package/src/routes/about/+page.svelte +7 -7
  62. package/src/routes/api/mock-csp/+server.js +3 -3
  63. package/src/routes/consultation/+page.svelte +5 -5
  64. package/src/routes/contact/+page.svelte +5 -5
  65. package/src/routes/foss-spotlight/+page.server.js +2 -2
  66. package/src/routes/foss-spotlight/+page.svelte +7 -7
  67. package/src/routes/license/+page.server.js +2 -2
  68. package/src/routes/license/+page.svelte +7 -7
  69. package/src/routes/pgp/+page.server.js +2 -2
  70. package/src/routes/pgp/+page.svelte +7 -7
  71. package/src/routes/pgp/[key]/+server.js +9 -9
  72. package/src/routes/privacy/+page.server.js +2 -2
  73. package/src/routes/privacy/+page.svelte +7 -7
  74. package/src/routes/privacy-dashboard/+page.server.js +2 -2
  75. package/src/routes/privacy-dashboard/+page.svelte +8 -8
  76. package/src/routes/privacy-rights/+page.svelte +5 -5
  77. package/src/routes/status/+page.server.js +2 -2
  78. package/src/routes/terms-conditions/+page.server.js +2 -2
  79. package/src/routes/terms-conditions/+page.svelte +7 -7
  80. package/src/routes/terms-of-use/+page.server.js +2 -2
  81. package/src/routes/terms-of-use/+page.svelte +7 -7
  82. package/src/service-worker.js +86 -85
  83. package/static/disableSw.js +2 -2
  84. package/static/img/powered-by-proton.svg +1 -0
  85. package/static/offline.html +7 -7
  86. package/static/sitemap.xml +64 -4
  87. package/stylelint.config.js +56 -56
  88. package/svelte.config.js +6 -6
  89. package/tests/e2e/app.spec.js +25 -25
  90. package/tests/e2e/mobile.spec.js +18 -18
  91. package/tests/e2e/shared/helpers.js +4 -4
  92. package/tests/internal/auditCoverage.test.js +24 -24
  93. package/tests/unit/checkEnv.test.js +10 -10
  94. package/tests/unit/checkVersions.test.js +4 -4
  95. package/tests/unit/csp-report.test.js +24 -24
  96. package/tests/unit/demo.test.js +3 -3
  97. package/tests/unit/lib/utils/purify.test.js +12 -12
  98. package/tests/unit/routes/page.svelte.test.js +10 -10
  99. package/tests/unit/unregisterServiceWorker.test.js +5 -5
  100. package/tests/unit/utm.test.js +13 -13
  101. package/vite.config.js +5 -5
  102. package/vitest-setup-client.js +4 -4
  103. package/vitest.config.client.js +15 -15
  104. package/vitest.config.server.js +13 -13
package/svelte.config.js CHANGED
@@ -6,8 +6,8 @@ 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 adapter from "@sveltejs/adapter-netlify"; // Netlify adapter for deployment
10
- import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"; // Vite preprocessor for Svelte
9
+ import adapter from '@sveltejs/adapter-netlify'; // Netlify adapter for deployment
10
+ import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; // Vite preprocessor for Svelte
11
11
 
12
12
  const config = {
13
13
  // Preprocessors for enhanced functionality (vitePreprocess + mdsvex for Markdown support + svelte-preprocess for PostCSS)
@@ -15,7 +15,7 @@ const config = {
15
15
  vitePreprocess({
16
16
  postcss: true,
17
17
  mdsvex: {
18
- extensions: [".svx", ".md"],
18
+ extensions: ['.svx', '.md'],
19
19
  },
20
20
  }),
21
21
  ],
@@ -29,14 +29,14 @@ const config = {
29
29
 
30
30
  // Paths configuration for deployment
31
31
  paths: {
32
- base: "", // Always deploy to the root of the domain
32
+ base: '', // Always deploy to the root of the domain
33
33
  },
34
34
 
35
35
  prerender: {
36
36
  // Handle HTTP errors during prerendering
37
37
  handleHttpError: ({ path, _referrer, message }) => {
38
38
  // Paths to ignore and warn about
39
- const warnList = ["/...404"];
39
+ const warnList = ['/...404'];
40
40
 
41
41
  if (warnList.includes(path)) {
42
42
  console.warn(`Prerender error at path: ${path}, message: ${message}`);
@@ -50,7 +50,7 @@ const config = {
50
50
  },
51
51
 
52
52
  // File extensions for Svelte and mdsvex
53
- extensions: [".svelte", ".svx", ".md"], // Added .md for Markdown support
53
+ extensions: ['.svelte', '.svx', '.md'], // Added .md for Markdown support
54
54
  };
55
55
 
56
56
  export default config;
@@ -14,25 +14,25 @@ This file is part of Network Pro.
14
14
  * @updated 2025-05-29
15
15
  */
16
16
 
17
- import { expect, test } from "@playwright/test";
17
+ import { expect, test } from '@playwright/test';
18
18
  import {
19
19
  getFooter,
20
20
  getVisibleNav,
21
21
  setDesktopView,
22
22
  setMobileView,
23
- } from "./shared/helpers.js";
23
+ } from './shared/helpers.js';
24
24
 
25
25
  // Root route should load successfully with the correct title
26
- test.describe("Desktop Tests", () => {
27
- test("should load successfully with the correct title", async ({
26
+ test.describe('Desktop Tests', () => {
27
+ test('should load successfully with the correct title', async ({
28
28
  page,
29
29
  browserName,
30
30
  }) => {
31
- if (browserName === "webkit") test.skip();
31
+ if (browserName === 'webkit') test.skip();
32
32
 
33
33
  await setDesktopView(page);
34
- await page.goto("/");
35
- await page.waitForLoadState("load", { timeout: 60000 });
34
+ await page.goto('/');
35
+ await page.waitForLoadState('load', { timeout: 60000 });
36
36
  await expect(page).toHaveTitle(/Locking Down Networks/);
37
37
  });
38
38
 
@@ -41,21 +41,21 @@ test.describe("Desktop Tests", () => {
41
41
  page,
42
42
  }) => {
43
43
  await setDesktopView(page);
44
- await page.goto("/");
44
+ await page.goto('/');
45
45
 
46
46
  const nav = await getVisibleNav(page);
47
47
 
48
- const aboutLink = nav.getByRole("link", { name: "about" });
48
+ const aboutLink = nav.getByRole('link', { name: 'about' });
49
49
  await expect(aboutLink).toBeVisible();
50
- await expect(aboutLink).toHaveAttribute("href", "/about");
50
+ await expect(aboutLink).toHaveAttribute('href', '/about');
51
51
  });
52
52
 
53
53
  // Root route should display the footer properly
54
- test("should display the footer correctly", async ({ page }) => {
54
+ test('should display the footer correctly', async ({ page }) => {
55
55
  await setDesktopView(page);
56
- await page.goto("/");
56
+ await page.goto('/');
57
57
 
58
- const footer = page.locator("footer");
58
+ const footer = page.locator('footer');
59
59
  await expect(footer).toBeVisible();
60
60
  });
61
61
 
@@ -64,7 +64,7 @@ test.describe("Desktop Tests", () => {
64
64
  page,
65
65
  }) => {
66
66
  await setDesktopView(page);
67
- await page.goto("/about");
67
+ await page.goto('/about');
68
68
 
69
69
  const footer = getFooter(page);
70
70
  await expect(footer).toBeVisible();
@@ -73,39 +73,39 @@ test.describe("Desktop Tests", () => {
73
73
  // Root route should have a clickable "about" link
74
74
  test("should ensure the 'about' link is clickable", async ({ page }) => {
75
75
  await setDesktopView(page);
76
- await page.goto("/");
76
+ await page.goto('/');
77
77
 
78
78
  const nav = await getVisibleNav(page);
79
79
 
80
- const aboutLink = nav.getByRole("link", { name: "about" });
80
+ const aboutLink = nav.getByRole('link', { name: 'about' });
81
81
  await expect(aboutLink).toBeVisible({ timeout: 60000 });
82
82
  await aboutLink.click();
83
83
 
84
- await page.waitForURL("/about", { timeout: 60000 });
84
+ await page.waitForURL('/about', { timeout: 60000 });
85
85
  await expect(page).toHaveURL(/\/about/);
86
86
  });
87
87
  }); // End Desktop Tests
88
88
 
89
89
  // Root route should load successfully with the correct title (mobile)
90
- test.describe("Mobile Tests", () => {
91
- test("should load successfully with the correct title on mobile", async ({
90
+ test.describe('Mobile Tests', () => {
91
+ test('should load successfully with the correct title on mobile', async ({
92
92
  page,
93
93
  browserName,
94
94
  }) => {
95
- if (browserName === "webkit") test.skip();
95
+ if (browserName === 'webkit') test.skip();
96
96
 
97
97
  await setMobileView(page);
98
- await page.goto("/");
99
- await page.waitForLoadState("load", { timeout: 60000 });
98
+ await page.goto('/');
99
+ await page.waitForLoadState('load', { timeout: 60000 });
100
100
  await expect(page).toHaveTitle(/Locking Down Networks/);
101
101
  });
102
102
 
103
103
  // Root route should display headings properly on mobile
104
- test("should display main content correctly on mobile", async ({ page }) => {
104
+ test('should display main content correctly on mobile', async ({ page }) => {
105
105
  await setMobileView(page);
106
- await page.goto("/");
106
+ await page.goto('/');
107
107
 
108
- const mainHeading = page.locator("h1, h2");
108
+ const mainHeading = page.locator('h1, h2');
109
109
  await expect(mainHeading).toBeVisible();
110
110
  });
111
111
  });
@@ -14,20 +14,20 @@ This file is part of Network Pro.
14
14
  * @updated 2025-05-29
15
15
  */
16
16
 
17
- import { expect, test } from "@playwright/test";
18
- import { getFooter, getVisibleNav, setMobileView } from "./shared/helpers.js";
17
+ import { expect, test } from '@playwright/test';
18
+ import { getFooter, getVisibleNav, setMobileView } from './shared/helpers.js';
19
19
 
20
20
  // Mobile viewport smoke tests for the root route
21
- test.describe("Mobile Tests", () => {
22
- test("should display the main description text on mobile", async ({
21
+ test.describe('Mobile Tests', () => {
22
+ test('should display the main description text on mobile', async ({
23
23
  page,
24
24
  browserName,
25
25
  }) => {
26
- if (browserName === "webkit") test.skip();
26
+ if (browserName === 'webkit') test.skip();
27
27
 
28
28
  await setMobileView(page);
29
- await page.goto("/");
30
- await page.waitForLoadState("domcontentloaded", { timeout: 60000 });
29
+ await page.goto('/');
30
+ await page.waitForLoadState('domcontentloaded', { timeout: 60000 });
31
31
 
32
32
  const description = page.locator(
33
33
  'div.index-title1:has-text("Locking Down Networks")',
@@ -35,17 +35,17 @@ test.describe("Mobile Tests", () => {
35
35
  await expect(description).toBeVisible();
36
36
  });
37
37
 
38
- test("should display main content correctly on mobile", async ({
38
+ test('should display main content correctly on mobile', async ({
39
39
  page,
40
40
  browserName,
41
41
  }) => {
42
- if (browserName === "webkit") test.skip();
42
+ if (browserName === 'webkit') test.skip();
43
43
 
44
44
  await setMobileView(page);
45
- await page.goto("/");
46
- await page.waitForLoadState("domcontentloaded", { timeout: 60000 });
45
+ await page.goto('/');
46
+ await page.waitForLoadState('domcontentloaded', { timeout: 60000 });
47
47
 
48
- const mainHeading = page.locator("h1, h2");
48
+ const mainHeading = page.locator('h1, h2');
49
49
  await expect(mainHeading).toBeVisible();
50
50
  });
51
51
 
@@ -53,27 +53,27 @@ test.describe("Mobile Tests", () => {
53
53
  page,
54
54
  browserName,
55
55
  }) => {
56
- if (browserName === "webkit") test.skip();
56
+ if (browserName === 'webkit') test.skip();
57
57
 
58
58
  await setMobileView(page);
59
- await page.goto("/");
59
+ await page.goto('/');
60
60
 
61
61
  const nav = await getVisibleNav(page);
62
- const aboutLink = nav.getByRole("link", { name: "about" });
62
+ const aboutLink = nav.getByRole('link', { name: 'about' });
63
63
  await expect(aboutLink).toBeVisible({ timeout: 60000 });
64
64
 
65
65
  await aboutLink.click();
66
66
  await expect(page).toHaveURL(/\/about/);
67
67
  });
68
68
 
69
- test("should display the footer on /about (mobile)", async ({
69
+ test('should display the footer on /about (mobile)', async ({
70
70
  page,
71
71
  browserName,
72
72
  }) => {
73
- if (browserName === "webkit") test.skip();
73
+ if (browserName === 'webkit') test.skip();
74
74
 
75
75
  await setMobileView(page);
76
- await page.goto("/about");
76
+ await page.goto('/about');
77
77
 
78
78
  const footer = getFooter(page);
79
79
  await expect(footer).toBeVisible();
@@ -39,13 +39,13 @@ export async function setMobileView(page) {
39
39
  * @throws {Error} If no visible navigation is found.
40
40
  */
41
41
  export async function getVisibleNav(page) {
42
- const navHome = page.getByRole("navigation", { name: "Homepage navigation" });
43
- const navMain = page.getByRole("navigation", { name: "Main navigation" });
42
+ const navHome = page.getByRole('navigation', { name: 'Homepage navigation' });
43
+ const navMain = page.getByRole('navigation', { name: 'Main navigation' });
44
44
 
45
45
  if (await navHome.isVisible()) return navHome;
46
46
  if (await navMain.isVisible()) return navMain;
47
47
 
48
- throw new Error("No visible navigation element found.");
48
+ throw new Error('No visible navigation element found.');
49
49
  }
50
50
 
51
51
  /**
@@ -53,5 +53,5 @@ export async function getVisibleNav(page) {
53
53
  * @returns {import('@playwright/test').Locator}
54
54
  */
55
55
  export function getFooter(page) {
56
- return page.locator("footer");
56
+ return page.locator('footer');
57
57
  }
@@ -14,9 +14,9 @@ This file is part of Network Pro.
14
14
  * @updated 2025-06-01
15
15
  */
16
16
 
17
- import fs from "fs";
18
- import path from "path";
19
- import { describe, expect, it } from "vitest";
17
+ import fs from 'fs';
18
+ import path from 'path';
19
+ import { describe, expect, it } from 'vitest';
20
20
 
21
21
  /**
22
22
  * Recursively get all .js files in a directory
@@ -33,10 +33,10 @@ function getAllJsFiles(dir, { includeTests = false } = {}) {
33
33
  const stat = fs.statSync(fullPath);
34
34
  if (stat.isDirectory()) {
35
35
  results = results.concat(getAllJsFiles(fullPath, { includeTests }));
36
- } else if (file.endsWith(".js")) {
36
+ } else if (file.endsWith('.js')) {
37
37
  if (
38
38
  !includeTests &&
39
- (file.endsWith(".test.js") || file.endsWith(".spec.js"))
39
+ (file.endsWith('.test.js') || file.endsWith('.spec.js'))
40
40
  ) {
41
41
  continue;
42
42
  }
@@ -46,41 +46,41 @@ function getAllJsFiles(dir, { includeTests = false } = {}) {
46
46
  return results;
47
47
  }
48
48
 
49
- describe("auditCoverage", () => {
50
- it("should have corresponding test files for all JS modules", () => {
49
+ describe('auditCoverage', () => {
50
+ it('should have corresponding test files for all JS modules', () => {
51
51
  const allowList = new Set([
52
- "checkNode.js",
53
- "auditScripts.js",
54
- "vite.config.js",
55
- "svelte.config.js",
52
+ 'checkNode.js',
53
+ 'auditScripts.js',
54
+ 'vite.config.js',
55
+ 'svelte.config.js',
56
56
  ]);
57
57
 
58
- const srcFiles = getAllJsFiles(path.resolve("src"));
59
- const scriptsFiles = getAllJsFiles(path.resolve("scripts"));
58
+ const srcFiles = getAllJsFiles(path.resolve('src'));
59
+ const scriptsFiles = getAllJsFiles(path.resolve('scripts'));
60
60
  const allFiles = [...srcFiles, ...scriptsFiles].map((f) =>
61
61
  path
62
62
  .relative(process.cwd(), f)
63
- .replace(/\\/g, "/") // Normalize Windows slashes
64
- .replace(/^src\//, "")
65
- .replace(/^scripts\//, "")
66
- .replace(/\.js$/, ""),
63
+ .replace(/\\/g, '/') // Normalize Windows slashes
64
+ .replace(/^src\//, '')
65
+ .replace(/^scripts\//, '')
66
+ .replace(/\.js$/, ''),
67
67
  );
68
68
 
69
- const testFiles = getAllJsFiles(path.resolve("tests/unit"), {
69
+ const testFiles = getAllJsFiles(path.resolve('tests/unit'), {
70
70
  includeTests: true,
71
71
  });
72
72
 
73
73
  const testFilesNormalized = testFiles.map((f) =>
74
74
  path
75
- .relative(path.resolve("tests/unit"), f)
76
- .replace(/\\/g, "/")
77
- .replace(/\.test\.js$|\.spec\.js$/, ""),
75
+ .relative(path.resolve('tests/unit'), f)
76
+ .replace(/\\/g, '/')
77
+ .replace(/\.test\.js$|\.spec\.js$/, ''),
78
78
  );
79
79
 
80
80
  const testedNames = new Set(testFilesNormalized);
81
81
 
82
82
  const untested = allFiles.filter((file) => {
83
- if (file.startsWith("tests/")) return false;
83
+ if (file.startsWith('tests/')) return false;
84
84
  if ([...allowList].some((allowed) => file.endsWith(allowed)))
85
85
  return false;
86
86
  return !testedNames.has(file);
@@ -88,8 +88,8 @@ describe("auditCoverage", () => {
88
88
 
89
89
  if (untested.length > 0) {
90
90
  console.warn(
91
- "\n[WARN] The following JS modules do not have corresponding tests:\n" +
92
- untested.map((f) => ` - ${f}`).join("\n"),
91
+ '\n[WARN] The following JS modules do not have corresponding tests:\n' +
92
+ untested.map((f) => ` - ${f}`).join('\n'),
93
93
  );
94
94
  }
95
95
 
@@ -10,10 +10,10 @@ This file is part of Network Pro.
10
10
  * Unit test for scripts/checkEnv.js
11
11
  */
12
12
 
13
- import { afterEach, describe, expect, it } from "vitest";
14
- import { checkEnv } from "../../scripts/checkEnv.js";
13
+ import { afterEach, describe, expect, it } from 'vitest';
14
+ import { checkEnv } from '../../scripts/checkEnv.js';
15
15
 
16
- describe("checkEnv()", () => {
16
+ describe('checkEnv()', () => {
17
17
  const originalEnv = process.env.ENV_MODE;
18
18
 
19
19
  afterEach(() => {
@@ -27,23 +27,23 @@ describe("checkEnv()", () => {
27
27
  it("should default to 'dev' if ENV_MODE is not set", () => {
28
28
  delete process.env.ENV_MODE;
29
29
  const result = checkEnv();
30
- expect(result.mode).toBe("dev");
30
+ expect(result.mode).toBe('dev');
31
31
  expect(result.valid).toBe(true);
32
32
  expect(result.wasDefaulted).toBe(true);
33
33
  });
34
34
 
35
- it("should validate a correct ENV_MODE", () => {
36
- process.env.ENV_MODE = "ci";
35
+ it('should validate a correct ENV_MODE', () => {
36
+ process.env.ENV_MODE = 'ci';
37
37
  const result = checkEnv();
38
- expect(result.mode).toBe("ci");
38
+ expect(result.mode).toBe('ci');
39
39
  expect(result.valid).toBe(true);
40
40
  expect(result.wasDefaulted).toBe(false);
41
41
  });
42
42
 
43
- it("should return invalid for an unknown ENV_MODE", () => {
44
- process.env.ENV_MODE = "banana";
43
+ it('should return invalid for an unknown ENV_MODE', () => {
44
+ process.env.ENV_MODE = 'banana';
45
45
  const result = checkEnv();
46
46
  expect(result.valid).toBe(false);
47
- expect(result.allowed).toContain("dev");
47
+ expect(result.allowed).toContain('dev');
48
48
  });
49
49
  });
@@ -10,11 +10,11 @@ This file is part of Network Pro.
10
10
  * Unit test for scripts/checkVersions.js
11
11
  */
12
12
 
13
- import { describe, expect, it } from "vitest";
14
- import { checkVersions } from "../../scripts/checkVersions.js";
13
+ import { describe, expect, it } from 'vitest';
14
+ import { checkVersions } from '../../scripts/checkVersions.js';
15
15
 
16
- describe("checkVersions()", () => {
17
- it("should match current Node and NPM versions to engine ranges", () => {
16
+ describe('checkVersions()', () => {
17
+ it('should match current Node and NPM versions to engine ranges', () => {
18
18
  const result = checkVersions();
19
19
 
20
20
  expect(result.nodeVersion).toMatch(/^v\d+\.\d+\.\d+$/);
@@ -17,28 +17,28 @@ This file is part of Network Pro.
17
17
  /** @file Unit tests for edge-functions/csp-report.js using Vitest */
18
18
  /** @typedef {import('vitest').TestContext} TestContext */
19
19
 
20
- import { beforeEach, describe, expect, it, vi } from "vitest";
21
- import handler from "../../netlify/edge-functions/csp-report.js";
20
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
21
+ import handler from '../../netlify/edge-functions/csp-report.js';
22
22
 
23
23
  // 🧪 Mock fetch used by sendToNtfy inside the Edge Function
24
24
  global.fetch = vi.fn(() =>
25
- Promise.resolve({ ok: true, status: 200, text: () => "OK" }),
25
+ Promise.resolve({ ok: true, status: 200, text: () => 'OK' }),
26
26
  );
27
27
 
28
- describe("csp-report.js", () => {
28
+ describe('csp-report.js', () => {
29
29
  beforeEach(() => {
30
30
  vi.clearAllMocks();
31
31
  });
32
32
 
33
- it("should handle a valid CSP report", async () => {
34
- const req = new Request("http://localhost/api/csp-report", {
35
- method: "POST",
36
- headers: { "Content-Type": "application/json" },
33
+ it('should handle a valid CSP report', async () => {
34
+ const req = new Request('http://localhost/api/csp-report', {
35
+ method: 'POST',
36
+ headers: { 'Content-Type': 'application/json' },
37
37
  body: JSON.stringify({
38
- "csp-report": {
39
- "document-uri": "https://example.com",
40
- "violated-directive": "script-src",
41
- "blocked-uri": "https://malicious.site",
38
+ 'csp-report': {
39
+ 'document-uri': 'https://example.com',
40
+ 'violated-directive': 'script-src',
41
+ 'blocked-uri': 'https://malicious.site',
42
42
  },
43
43
  }),
44
44
  });
@@ -47,22 +47,22 @@ describe("csp-report.js", () => {
47
47
  expect(res.status).toBe(204);
48
48
  });
49
49
 
50
- it("should reject non-POST requests", async () => {
51
- const req = new Request("http://localhost/api/csp-report", {
52
- method: "GET",
50
+ it('should reject non-POST requests', async () => {
51
+ const req = new Request('http://localhost/api/csp-report', {
52
+ method: 'GET',
53
53
  });
54
54
 
55
55
  const res = await handler(req, {});
56
56
  const text = await res.text();
57
57
  expect(res.status).toBe(405);
58
- expect(text).toContain("Method Not Allowed");
58
+ expect(text).toContain('Method Not Allowed');
59
59
  });
60
60
 
61
- it("should handle malformed JSON", async () => {
62
- const badJson = "{ invalid json }";
63
- const req = new Request("http://localhost/api/csp-report", {
64
- method: "POST",
65
- headers: { "Content-Type": "application/json" },
61
+ it('should handle malformed JSON', async () => {
62
+ const badJson = '{ invalid json }';
63
+ const req = new Request('http://localhost/api/csp-report', {
64
+ method: 'POST',
65
+ headers: { 'Content-Type': 'application/json' },
66
66
  body: badJson,
67
67
  });
68
68
 
@@ -70,9 +70,9 @@ describe("csp-report.js", () => {
70
70
  expect(res.status).toBe(204); // The current handler swallows errors silently
71
71
  });
72
72
 
73
- it("should handle missing body", async () => {
74
- const req = new Request("http://localhost/api/csp-report", {
75
- method: "POST",
73
+ it('should handle missing body', async () => {
74
+ const req = new Request('http://localhost/api/csp-report', {
75
+ method: 'POST',
76
76
  });
77
77
 
78
78
  const res = await handler(req, {});
@@ -6,10 +6,10 @@ 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 { describe, expect, it } from "vitest";
9
+ import { describe, expect, it } from 'vitest';
10
10
 
11
- describe("sum test", () => {
12
- it("adds 1 + 2 to equal 3", () => {
11
+ describe('sum test', () => {
12
+ it('adds 1 + 2 to equal 3', () => {
13
13
  expect(1 + 2).toBe(3);
14
14
  });
15
15
  });
@@ -14,37 +14,37 @@ This file is part of Network Pro.
14
14
  * @updated 2025-06-01
15
15
  */
16
16
 
17
- import { describe, expect, it } from "vitest";
18
- import { sanitizeHtml } from "../../../../src/lib/utils/purify.js";
17
+ import { describe, expect, it } from 'vitest';
18
+ import { sanitizeHtml } from '../../../../src/lib/utils/purify.js';
19
19
 
20
- describe("sanitizeHtml", () => {
21
- it("removes dangerous tags like <script>", async () => {
20
+ describe('sanitizeHtml', () => {
21
+ it('removes dangerous tags like <script>', async () => {
22
22
  const dirty = `<div>Hello <script>alert("XSS")</script> world!</div>`;
23
23
  const clean = await sanitizeHtml(dirty);
24
- expect(clean).toBe("<div>Hello world!</div>");
24
+ expect(clean).toBe('<div>Hello world!</div>');
25
25
  }); // timeout in ms
26
26
 
27
- it("preserves safe markup like <strong>", async () => {
27
+ it('preserves safe markup like <strong>', async () => {
28
28
  const dirty = `<p>This is <strong>important</strong>.</p>`;
29
29
  const clean = await sanitizeHtml(dirty);
30
- expect(clean).toBe("<p>This is <strong>important</strong>.</p>");
30
+ expect(clean).toBe('<p>This is <strong>important</strong>.</p>');
31
31
  });
32
32
 
33
- it("removes dangerous attributes like onerror", async () => {
33
+ it('removes dangerous attributes like onerror', async () => {
34
34
  const dirty = `<img src="x" onerror="alert(1)">`;
35
35
  const clean = await sanitizeHtml(dirty);
36
- expect(clean).toBe("<img>");
36
+ expect(clean).toBe('<img>');
37
37
  });
38
38
 
39
- it("keeps valid external links", async () => {
39
+ it('keeps valid external links', async () => {
40
40
  const dirty = `<a href="https://example.com">Click</a>`;
41
41
  const clean = await sanitizeHtml(dirty);
42
42
  expect(clean).toBe('<a href="https://example.com">Click</a>');
43
43
  });
44
44
 
45
- it("blocks javascript: URLs", async () => {
45
+ it('blocks javascript: URLs', async () => {
46
46
  const dirty = `<a href="javascript:alert('XSS')">bad</a>`;
47
47
  const clean = await sanitizeHtml(dirty);
48
- expect(clean).toBe("<a>bad</a>");
48
+ expect(clean).toBe('<a>bad</a>');
49
49
  });
50
50
  });
@@ -6,25 +6,25 @@ 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 "@testing-library/jest-dom/vitest";
10
- import { render, screen } from "@testing-library/svelte";
11
- import { describe, expect, test } from "vitest";
12
- import Page from "../../../src/routes/+page.svelte";
9
+ import '@testing-library/jest-dom/vitest';
10
+ import { render, screen } from '@testing-library/svelte';
11
+ import { describe, expect, test } from 'vitest';
12
+ import Page from '../../../src/routes/+page.svelte';
13
13
 
14
- describe("/+page.svelte", () => {
15
- test("should render the home page section", () => {
14
+ describe('/+page.svelte', () => {
15
+ test('should render the home page section', () => {
16
16
  const mockData = {
17
- pathname: "/", // Required because layout uses it
17
+ pathname: '/', // Required because layout uses it
18
18
  meta: {
19
19
  title:
20
- "Locking Down Networks, Unlocking Confidence™ | Security, Networking, Privacy — Network Pro™",
20
+ 'Locking Down Networks, Unlocking Confidence™ | Security, Networking, Privacy — Network Pro™',
21
21
  description:
22
- "Locking Down Networks, Unlocking Confidence™ | Security, Networking, Privacy — Network Pro™",
22
+ 'Locking Down Networks, Unlocking Confidence™ | Security, Networking, Privacy — Network Pro™',
23
23
  },
24
24
  };
25
25
 
26
26
  render(Page, { props: { data: mockData } });
27
27
 
28
- expect(screen.getByTestId("home-page")).toBeInTheDocument();
28
+ expect(screen.getByTestId('home-page')).toBeInTheDocument();
29
29
  });
30
30
  });