@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.
- package/CHANGELOG.md +64 -1
- package/README.md +8 -8
- package/cspell.json +1 -0
- package/eslint.config.mjs +48 -48
- package/jsconfig.template.jsonc +3 -1
- package/netlify/edge-functions/csp-report.js +31 -31
- package/package.json +6 -6
- package/playwright.config.js +14 -14
- package/postcss.config.mjs +1 -1
- package/scripts/auditScripts.js +16 -16
- package/scripts/bundleCss.js +5 -5
- package/scripts/checkEnv.js +6 -6
- package/scripts/checkNode.js +10 -10
- package/scripts/checkVersions.js +6 -6
- package/scripts/flattenHeaders.js +13 -13
- package/scripts/generateTest.js +5 -5
- package/scripts/openReport.js +3 -3
- package/scripts/validateHeaders.js +13 -13
- package/src/hooks.client.ts +1 -1
- package/src/hooks.server.js +31 -31
- package/src/lib/components/Badges.svelte +9 -9
- package/src/lib/components/CodeBlock.svelte +13 -0
- package/src/lib/components/ContainerSection.svelte +1 -1
- package/src/lib/components/FullWidthSection.svelte +3 -3
- package/src/lib/components/LegalNav.svelte +6 -6
- package/src/lib/components/Logo.svelte +9 -9
- package/src/lib/components/MetaTags.svelte +3 -3
- package/src/lib/components/PWAInstallButton.svelte +4 -4
- package/src/lib/components/RedirectPage.svelte +4 -4
- package/src/lib/components/SocialMedia.svelte +16 -16
- package/src/lib/components/foss/FossItemContent.svelte +18 -18
- package/src/lib/components/layout/Footer.svelte +17 -17
- package/src/lib/components/layout/HeaderDefault.svelte +16 -16
- package/src/lib/components/layout/HeaderHome.svelte +14 -14
- package/src/lib/images.js +34 -34
- package/src/lib/index.js +15 -15
- package/src/lib/meta.js +29 -29
- package/src/lib/pages/AboutContent.svelte +24 -24
- package/src/lib/pages/FossContent.svelte +12 -12
- package/src/lib/pages/HomeContent.svelte +6 -6
- package/src/lib/pages/LicenseContent.svelte +38 -38
- package/src/lib/pages/PGPContent.svelte +61 -29
- package/src/lib/pages/PrivacyContent.svelte +39 -39
- package/src/lib/pages/PrivacyDashboard.svelte +12 -12
- package/src/lib/pages/TermsConditionsContent.svelte +28 -28
- package/src/lib/pages/TermsUseContent.svelte +26 -26
- package/src/lib/registerServiceWorker.js +25 -25
- package/src/lib/stores/posthog.js +13 -13
- package/src/lib/stores/trackingPreferences.js +19 -19
- package/src/lib/styles/css/default.css +5 -1
- package/src/lib/styles/global.min.css +1 -1
- package/src/lib/unregisterServiceWorker.js +1 -1
- package/src/lib/utils/purify.js +4 -4
- package/src/lib/utils/utm.js +2 -2
- package/src/routes/+error.svelte +4 -4
- package/src/routes/+layout.js +6 -6
- package/src/routes/+layout.svelte +29 -29
- package/src/routes/+page.server.js +2 -2
- package/src/routes/+page.svelte +9 -9
- package/src/routes/about/+page.server.js +2 -2
- package/src/routes/about/+page.svelte +7 -7
- package/src/routes/api/mock-csp/+server.js +3 -3
- package/src/routes/consultation/+page.svelte +5 -5
- package/src/routes/contact/+page.svelte +5 -5
- package/src/routes/foss-spotlight/+page.server.js +2 -2
- package/src/routes/foss-spotlight/+page.svelte +7 -7
- package/src/routes/license/+page.server.js +2 -2
- package/src/routes/license/+page.svelte +7 -7
- package/src/routes/pgp/+page.server.js +2 -2
- package/src/routes/pgp/+page.svelte +7 -7
- package/src/routes/pgp/[key]/+server.js +9 -9
- package/src/routes/privacy/+page.server.js +2 -2
- package/src/routes/privacy/+page.svelte +7 -7
- package/src/routes/privacy-dashboard/+page.server.js +2 -2
- package/src/routes/privacy-dashboard/+page.svelte +8 -8
- package/src/routes/privacy-rights/+page.svelte +5 -5
- package/src/routes/status/+page.server.js +2 -2
- package/src/routes/terms-conditions/+page.server.js +2 -2
- package/src/routes/terms-conditions/+page.svelte +7 -7
- package/src/routes/terms-of-use/+page.server.js +2 -2
- package/src/routes/terms-of-use/+page.svelte +7 -7
- package/src/service-worker.js +86 -85
- package/static/disableSw.js +2 -2
- package/static/img/powered-by-proton.svg +1 -0
- package/static/offline.html +7 -7
- package/static/sitemap.xml +64 -4
- package/stylelint.config.js +56 -56
- package/svelte.config.js +6 -6
- package/tests/e2e/app.spec.js +25 -25
- package/tests/e2e/mobile.spec.js +18 -18
- package/tests/e2e/shared/helpers.js +4 -4
- package/tests/internal/auditCoverage.test.js +24 -24
- package/tests/unit/checkEnv.test.js +10 -10
- package/tests/unit/checkVersions.test.js +4 -4
- package/tests/unit/csp-report.test.js +24 -24
- package/tests/unit/demo.test.js +3 -3
- package/tests/unit/lib/utils/purify.test.js +12 -12
- package/tests/unit/routes/page.svelte.test.js +10 -10
- package/tests/unit/unregisterServiceWorker.test.js +5 -5
- package/tests/unit/utm.test.js +13 -13
- package/vite.config.js +5 -5
- package/vitest-setup-client.js +4 -4
- package/vitest.config.client.js +15 -15
- 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
|
|
10
|
-
import { vitePreprocess } from
|
|
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: [
|
|
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:
|
|
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 = [
|
|
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: [
|
|
53
|
+
extensions: ['.svelte', '.svx', '.md'], // Added .md for Markdown support
|
|
54
54
|
};
|
|
55
55
|
|
|
56
56
|
export default config;
|
package/tests/e2e/app.spec.js
CHANGED
|
@@ -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
|
|
17
|
+
import { expect, test } from '@playwright/test';
|
|
18
18
|
import {
|
|
19
19
|
getFooter,
|
|
20
20
|
getVisibleNav,
|
|
21
21
|
setDesktopView,
|
|
22
22
|
setMobileView,
|
|
23
|
-
} from
|
|
23
|
+
} from './shared/helpers.js';
|
|
24
24
|
|
|
25
25
|
// Root route should load successfully with the correct title
|
|
26
|
-
test.describe(
|
|
27
|
-
test(
|
|
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 ===
|
|
31
|
+
if (browserName === 'webkit') test.skip();
|
|
32
32
|
|
|
33
33
|
await setDesktopView(page);
|
|
34
|
-
await page.goto(
|
|
35
|
-
await page.waitForLoadState(
|
|
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(
|
|
48
|
+
const aboutLink = nav.getByRole('link', { name: 'about' });
|
|
49
49
|
await expect(aboutLink).toBeVisible();
|
|
50
|
-
await expect(aboutLink).toHaveAttribute(
|
|
50
|
+
await expect(aboutLink).toHaveAttribute('href', '/about');
|
|
51
51
|
});
|
|
52
52
|
|
|
53
53
|
// Root route should display the footer properly
|
|
54
|
-
test(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
91
|
-
test(
|
|
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 ===
|
|
95
|
+
if (browserName === 'webkit') test.skip();
|
|
96
96
|
|
|
97
97
|
await setMobileView(page);
|
|
98
|
-
await page.goto(
|
|
99
|
-
await page.waitForLoadState(
|
|
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(
|
|
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(
|
|
108
|
+
const mainHeading = page.locator('h1, h2');
|
|
109
109
|
await expect(mainHeading).toBeVisible();
|
|
110
110
|
});
|
|
111
111
|
});
|
package/tests/e2e/mobile.spec.js
CHANGED
|
@@ -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
|
|
18
|
-
import { getFooter, getVisibleNav, setMobileView } from
|
|
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(
|
|
22
|
-
test(
|
|
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 ===
|
|
26
|
+
if (browserName === 'webkit') test.skip();
|
|
27
27
|
|
|
28
28
|
await setMobileView(page);
|
|
29
|
-
await page.goto(
|
|
30
|
-
await page.waitForLoadState(
|
|
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(
|
|
38
|
+
test('should display main content correctly on mobile', async ({
|
|
39
39
|
page,
|
|
40
40
|
browserName,
|
|
41
41
|
}) => {
|
|
42
|
-
if (browserName ===
|
|
42
|
+
if (browserName === 'webkit') test.skip();
|
|
43
43
|
|
|
44
44
|
await setMobileView(page);
|
|
45
|
-
await page.goto(
|
|
46
|
-
await page.waitForLoadState(
|
|
45
|
+
await page.goto('/');
|
|
46
|
+
await page.waitForLoadState('domcontentloaded', { timeout: 60000 });
|
|
47
47
|
|
|
48
|
-
const mainHeading = page.locator(
|
|
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 ===
|
|
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(
|
|
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(
|
|
69
|
+
test('should display the footer on /about (mobile)', async ({
|
|
70
70
|
page,
|
|
71
71
|
browserName,
|
|
72
72
|
}) => {
|
|
73
|
-
if (browserName ===
|
|
73
|
+
if (browserName === 'webkit') test.skip();
|
|
74
74
|
|
|
75
75
|
await setMobileView(page);
|
|
76
|
-
await page.goto(
|
|
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(
|
|
43
|
-
const navMain = page.getByRole(
|
|
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(
|
|
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(
|
|
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
|
|
18
|
-
import path from
|
|
19
|
-
import { describe, expect, it } from
|
|
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(
|
|
36
|
+
} else if (file.endsWith('.js')) {
|
|
37
37
|
if (
|
|
38
38
|
!includeTests &&
|
|
39
|
-
(file.endsWith(
|
|
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(
|
|
50
|
-
it(
|
|
49
|
+
describe('auditCoverage', () => {
|
|
50
|
+
it('should have corresponding test files for all JS modules', () => {
|
|
51
51
|
const allowList = new Set([
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
52
|
+
'checkNode.js',
|
|
53
|
+
'auditScripts.js',
|
|
54
|
+
'vite.config.js',
|
|
55
|
+
'svelte.config.js',
|
|
56
56
|
]);
|
|
57
57
|
|
|
58
|
-
const srcFiles = getAllJsFiles(path.resolve(
|
|
59
|
-
const scriptsFiles = getAllJsFiles(path.resolve(
|
|
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,
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
92
|
-
untested.map((f) => ` - ${f}`).join(
|
|
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
|
|
14
|
-
import { checkEnv } from
|
|
13
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
14
|
+
import { checkEnv } from '../../scripts/checkEnv.js';
|
|
15
15
|
|
|
16
|
-
describe(
|
|
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(
|
|
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(
|
|
36
|
-
process.env.ENV_MODE =
|
|
35
|
+
it('should validate a correct ENV_MODE', () => {
|
|
36
|
+
process.env.ENV_MODE = 'ci';
|
|
37
37
|
const result = checkEnv();
|
|
38
|
-
expect(result.mode).toBe(
|
|
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(
|
|
44
|
-
process.env.ENV_MODE =
|
|
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(
|
|
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
|
|
14
|
-
import { checkVersions } from
|
|
13
|
+
import { describe, expect, it } from 'vitest';
|
|
14
|
+
import { checkVersions } from '../../scripts/checkVersions.js';
|
|
15
15
|
|
|
16
|
-
describe(
|
|
17
|
-
it(
|
|
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
|
|
21
|
-
import handler from
|
|
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: () =>
|
|
25
|
+
Promise.resolve({ ok: true, status: 200, text: () => 'OK' }),
|
|
26
26
|
);
|
|
27
27
|
|
|
28
|
-
describe(
|
|
28
|
+
describe('csp-report.js', () => {
|
|
29
29
|
beforeEach(() => {
|
|
30
30
|
vi.clearAllMocks();
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
-
it(
|
|
34
|
-
const req = new Request(
|
|
35
|
-
method:
|
|
36
|
-
headers: {
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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(
|
|
51
|
-
const req = new Request(
|
|
52
|
-
method:
|
|
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(
|
|
58
|
+
expect(text).toContain('Method Not Allowed');
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
-
it(
|
|
62
|
-
const badJson =
|
|
63
|
-
const req = new Request(
|
|
64
|
-
method:
|
|
65
|
-
headers: {
|
|
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(
|
|
74
|
-
const req = new Request(
|
|
75
|
-
method:
|
|
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, {});
|
package/tests/unit/demo.test.js
CHANGED
|
@@ -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
|
|
9
|
+
import { describe, expect, it } from 'vitest';
|
|
10
10
|
|
|
11
|
-
describe(
|
|
12
|
-
it(
|
|
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
|
|
18
|
-
import { sanitizeHtml } from
|
|
17
|
+
import { describe, expect, it } from 'vitest';
|
|
18
|
+
import { sanitizeHtml } from '../../../../src/lib/utils/purify.js';
|
|
19
19
|
|
|
20
|
-
describe(
|
|
21
|
-
it(
|
|
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(
|
|
24
|
+
expect(clean).toBe('<div>Hello world!</div>');
|
|
25
25
|
}); // timeout in ms
|
|
26
26
|
|
|
27
|
-
it(
|
|
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(
|
|
30
|
+
expect(clean).toBe('<p>This is <strong>important</strong>.</p>');
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
-
it(
|
|
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(
|
|
36
|
+
expect(clean).toBe('<img>');
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
-
it(
|
|
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(
|
|
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(
|
|
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
|
|
10
|
-
import { render, screen } from
|
|
11
|
-
import { describe, expect, test } from
|
|
12
|
-
import Page from
|
|
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(
|
|
15
|
-
test(
|
|
14
|
+
describe('/+page.svelte', () => {
|
|
15
|
+
test('should render the home page section', () => {
|
|
16
16
|
const mockData = {
|
|
17
|
-
pathname:
|
|
17
|
+
pathname: '/', // Required because layout uses it
|
|
18
18
|
meta: {
|
|
19
19
|
title:
|
|
20
|
-
|
|
20
|
+
'Locking Down Networks, Unlocking Confidence™ | Security, Networking, Privacy — Network Pro™',
|
|
21
21
|
description:
|
|
22
|
-
|
|
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(
|
|
28
|
+
expect(screen.getByTestId('home-page')).toBeInTheDocument();
|
|
29
29
|
});
|
|
30
30
|
});
|