@networkpro/web 1.14.2 → 1.15.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 (41) hide show
  1. package/CHANGELOG.md +78 -1
  2. package/README.md +6 -4
  3. package/_redirects +4 -2
  4. package/cspell.json +2 -0
  5. package/netlify/edge-functions/csp-report.js +22 -15
  6. package/package.json +3 -4
  7. package/scripts/testRedirects.js +84 -0
  8. package/src/app.html +1 -1
  9. package/src/lib/components/RedirectPage.svelte +2 -34
  10. package/src/lib/components/layout/HeaderDefault.svelte +8 -3
  11. package/src/lib/components/layout/HeaderHome.svelte +8 -3
  12. package/src/lib/pages/AboutContent.svelte +1 -1
  13. package/src/lib/pages/HomeContent.svelte +3 -9
  14. package/src/lib/pages/LicenseContent.svelte +1 -2
  15. package/src/lib/pages/PrivacyContent.svelte +45 -31
  16. package/src/lib/pages/PrivacyDashboard.svelte +0 -1
  17. package/src/lib/pages/TermsConditionsContent.svelte +2 -2
  18. package/src/lib/stores/trackingPreferences.js +6 -0
  19. package/src/lib/styles/css/default.css +31 -4
  20. package/src/lib/styles/global.min.css +1 -1
  21. package/src/lib/utils/getUTMParams.js +43 -0
  22. package/src/lib/utils/purify.js +1 -1
  23. package/src/lib/utils/redirect.js +52 -0
  24. package/src/lib/utils/utm.js +31 -8
  25. package/src/routes/consultation/+page.svelte +16 -2
  26. package/src/routes/contact/+page.svelte +17 -3
  27. package/src/routes/links/+page.svelte +24 -5
  28. package/src/routes/posts/+page.svelte +24 -5
  29. package/src/routes/privacy-rights/+page.svelte +19 -3
  30. package/tests/unit/{unregisterServiceWorker.test.js → client/lib/unregisterServiceWorker.test.js} +2 -2
  31. package/tests/unit/client/lib/utils/redirect.test.js +80 -0
  32. package/tests/unit/client/lib/utils/utm.test.js +59 -0
  33. package/tests/unit/{routes → client/routes}/page.svelte.test.js +2 -2
  34. package/tests/unit/{checkEnv.test.js → server/checkEnv.test.js} +2 -2
  35. package/tests/unit/{checkVersions.test.js → server/checkVersions.test.js} +2 -2
  36. package/tests/unit/{csp-report.test.js → server/csp-report.test.js} +2 -2
  37. package/tests/{internal → unit/server/internal}/auditCoverage.test.js +13 -6
  38. package/tests/unit/{lib → server/lib}/utils/purify.test.js +2 -2
  39. package/vitest.config.client.js +1 -4
  40. package/vitest.config.server.js +1 -1
  41. package/tests/unit/utm.test.js +0 -49
@@ -1,5 +1,5 @@
1
1
  /* ==========================================================================
2
- tests/unit/unregisterServiceWorker.test.js
2
+ tests/unit/client/lib/unregisterServiceWorker.test.js
3
3
 
4
4
  Copyright © 2025 Network Pro Strategies (Network Pro™)
5
5
  SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
@@ -7,7 +7,7 @@ This file is part of Network Pro.
7
7
  ========================================================================== */
8
8
 
9
9
  import { beforeEach, describe, expect, it, vi } from 'vitest';
10
- import { unregisterServiceWorker } from '../../src/lib/unregisterServiceWorker.js';
10
+ import { unregisterServiceWorker } from '../../../../src/lib/unregisterServiceWorker.js';
11
11
 
12
12
  describe('unregisterServiceWorker()', () => {
13
13
  beforeEach(() => {
@@ -0,0 +1,80 @@
1
+ /* ==========================================================================
2
+ tests/unit/client/lib/utils/redirect.test.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 redirect.test.js
11
+ * @description Unit test for src/lib/utils/redirect.js
12
+ * @module tests/unit/lib/util/
13
+ * @author SunDevil311
14
+ * @updated 2025-07-01
15
+ */
16
+
17
+ import { redirectWithBrowserAwareness } from '$lib/utils/redirect.js';
18
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
19
+
20
+ describe('redirectWithBrowserAwareness', () => {
21
+ let originalLocation;
22
+ let originalNavigator;
23
+
24
+ /** @type {{ url: string; delay: number }} */
25
+ const commonMocks = {
26
+ url: 'https://example.com',
27
+ delay: 1,
28
+ };
29
+
30
+ beforeEach(() => {
31
+ // Preserve globals
32
+ originalLocation = window.location;
33
+ originalNavigator = global.navigator;
34
+
35
+ // Stub window.location.replace
36
+ delete window.location;
37
+ window.location = {
38
+ replace: vi.fn(),
39
+ };
40
+ });
41
+
42
+ afterEach(() => {
43
+ // Restore globals
44
+ window.location = originalLocation;
45
+ global.navigator = originalNavigator;
46
+ vi.clearAllMocks();
47
+ });
48
+
49
+ it('should redirect immediately in Firefox', () => {
50
+ global.navigator = {
51
+ userAgent:
52
+ 'Mozilla/5.0 (Windows NT 10.0; rv:99.0) Gecko/20100101 Firefox/99.0',
53
+ };
54
+
55
+ redirectWithBrowserAwareness(commonMocks.url, commonMocks.delay);
56
+
57
+ expect(window.location.replace).toHaveBeenCalledWith(commonMocks.url);
58
+ });
59
+
60
+ it('should redirect after delay in non-Firefox', () => {
61
+ vi.useFakeTimers();
62
+
63
+ global.navigator = {
64
+ userAgent:
65
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0 Safari/537.36',
66
+ };
67
+
68
+ redirectWithBrowserAwareness(commonMocks.url, commonMocks.delay);
69
+
70
+ expect(window.location.replace).not.toHaveBeenCalled();
71
+
72
+ vi.advanceTimersByTime(commonMocks.delay * 1000);
73
+
74
+ expect(window.location.replace).toHaveBeenCalledWith(commonMocks.url);
75
+
76
+ vi.useRealTimers();
77
+ });
78
+ });
79
+
80
+ // cspell:ignore khtml
@@ -0,0 +1,59 @@
1
+ /* ==========================================================================
2
+ tests/unit/client/lib/utils/utm.test.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
+ // Mock SvelteKit environment and store
10
+ vi.mock('$app/environment', () => ({ browser: true }));
11
+
12
+ import { writable } from 'svelte/store';
13
+
14
+ vi.mock('$app/stores', () => {
15
+ const mockPageStore = writable({
16
+ url: {
17
+ pathname: '/contact',
18
+ },
19
+ });
20
+
21
+ return {
22
+ getStores: () => ({
23
+ page: mockPageStore,
24
+ }),
25
+ };
26
+ });
27
+
28
+ import { appendUTM } from '$lib/utils/utm.js';
29
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
30
+
31
+ describe('appendUTM', () => {
32
+ const originalWindow = globalThis.window;
33
+
34
+ beforeEach(() => {
35
+ globalThis.window = {
36
+ location: { search: '' },
37
+ };
38
+ });
39
+
40
+ afterEach(() => {
41
+ globalThis.window = originalWindow;
42
+ });
43
+
44
+ it('should return URL with utm parameters for /contact', () => {
45
+ const url = 'https://example.com';
46
+ const result = appendUTM(url);
47
+ expect(result).toBe(
48
+ 'https://example.com?utm_source=netwk.pro&utm_medium=redirect&utm_campaign=contact',
49
+ );
50
+ });
51
+
52
+ it('should append using & if URL already has query params', () => {
53
+ const url = 'https://example.com?existing=value';
54
+ const result = appendUTM(url);
55
+ expect(result).toBe(
56
+ 'https://example.com?existing=value&utm_source=netwk.pro&utm_medium=redirect&utm_campaign=contact',
57
+ );
58
+ });
59
+ });
@@ -1,5 +1,5 @@
1
1
  /* ==========================================================================
2
- tests/unit/routes/page.svelte.test.js
2
+ tests/unit/client/routes/page.svelte.test.js
3
3
 
4
4
  Copyright © 2025 Network Pro Strategies (Network Pro™)
5
5
  SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
@@ -9,7 +9,7 @@ This file is part of Network Pro.
9
9
  import '@testing-library/jest-dom/vitest';
10
10
  import { render, screen } from '@testing-library/svelte';
11
11
  import { describe, expect, test } from 'vitest';
12
- import Page from '../../../src/routes/+page.svelte';
12
+ import Page from '../../../../src/routes/+page.svelte';
13
13
 
14
14
  describe('/+page.svelte', () => {
15
15
  test('should render the home page section', () => {
@@ -1,5 +1,5 @@
1
1
  /* ==========================================================================
2
- tests/unit/checkEnv.test.js
2
+ tests/unit/server/checkEnv.test.js
3
3
 
4
4
  Copyright © 2025 Network Pro Strategies (Network Pro™)
5
5
  SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
@@ -11,7 +11,7 @@ This file is part of Network Pro.
11
11
  */
12
12
 
13
13
  import { afterEach, describe, expect, it } from 'vitest';
14
- import { checkEnv } from '../../scripts/checkEnv.js';
14
+ import { checkEnv } from '../../../scripts/checkEnv.js';
15
15
 
16
16
  describe('checkEnv()', () => {
17
17
  const originalEnv = process.env.ENV_MODE;
@@ -1,5 +1,5 @@
1
1
  /* ==========================================================================
2
- tests/unit/checkVersions.test.js
2
+ tests/unit/server/checkVersions.test.js
3
3
 
4
4
  Copyright © 2025 Network Pro Strategies (Network Pro™)
5
5
  SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
@@ -11,7 +11,7 @@ This file is part of Network Pro.
11
11
  */
12
12
 
13
13
  import { describe, expect, it } from 'vitest';
14
- import { checkVersions } from '../../scripts/checkVersions.js';
14
+ import { checkVersions } from '../../../scripts/checkVersions.js';
15
15
 
16
16
  describe('checkVersions()', () => {
17
17
  it('should match current Node and NPM versions to engine ranges', () => {
@@ -1,5 +1,5 @@
1
1
  /* ==========================================================================
2
- tests/unit/csp-report.test.js
2
+ tests/unit/server/csp-report.test.js
3
3
 
4
4
  Copyright © 2025 Network Pro Strategies (Network Pro™)
5
5
  SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
@@ -18,7 +18,7 @@ This file is part of Network Pro.
18
18
  /** @typedef {import('vitest').TestContext} TestContext */
19
19
 
20
20
  import { beforeEach, describe, expect, it, vi } from 'vitest';
21
- import handler from '../../netlify/edge-functions/csp-report.js';
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(() =>
@@ -1,5 +1,5 @@
1
1
  /* ==========================================================================
2
- tests/internal/auditCoverage.test.js
2
+ tests/unit/server/internal/auditCoverage.test.js
3
3
 
4
4
  Copyright © 2025 Network Pro Strategies (Network Pro™)
5
5
  SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
@@ -8,10 +8,11 @@ This file is part of Network Pro.
8
8
 
9
9
  /**
10
10
  * @file auditCoverage.test.js
11
- * @description Scans all .js files in src/ and scripts/ for matching unit test
11
+ * @description Scans all .js files in src/ and scripts/ for matching unit
12
+ * tests
12
13
  * @module tests/internal
13
14
  * @author SunDevil311
14
- * @updated 2025-06-01
15
+ * @updated 2025-07-01
15
16
  */
16
17
 
17
18
  import fs from 'fs';
@@ -60,20 +61,26 @@ describe('auditCoverage', () => {
60
61
  const allFiles = [...srcFiles, ...scriptsFiles].map((f) =>
61
62
  path
62
63
  .relative(process.cwd(), f)
63
- .replace(/\\/g, '/') // Normalize Windows slashes
64
+ .replace(/\\/g, '/')
64
65
  .replace(/^src\//, '')
65
66
  .replace(/^scripts\//, '')
66
67
  .replace(/\.js$/, ''),
67
68
  );
68
69
 
69
- const testFiles = getAllJsFiles(path.resolve('tests/unit'), {
70
+ const clientTests = getAllJsFiles(path.resolve('tests/unit/client'), {
71
+ includeTests: true,
72
+ });
73
+ const serverTests = getAllJsFiles(path.resolve('tests/unit/server'), {
70
74
  includeTests: true,
71
75
  });
76
+ const testFiles = [...clientTests, ...serverTests];
72
77
 
73
78
  const testFilesNormalized = testFiles.map((f) =>
74
79
  path
75
- .relative(path.resolve('tests/unit'), f)
80
+ .relative(process.cwd(), f)
76
81
  .replace(/\\/g, '/')
82
+ .replace(/^tests\/unit\/client\//, '')
83
+ .replace(/^tests\/unit\/server\//, '')
77
84
  .replace(/\.test\.js$|\.spec\.js$/, ''),
78
85
  );
79
86
 
@@ -1,5 +1,5 @@
1
1
  /* ==========================================================================
2
- tests/unit/lib/utils/purify.test.js
2
+ tests/unit/server/lib/utils/purify.test.js
3
3
 
4
4
  Copyright © 2025 Network Pro Strategies (Network Pro™)
5
5
  SPDX-License-Identifier: CC-BY-4.0 OR GPL-3.0-or-later
@@ -15,7 +15,7 @@ This file is part of Network Pro.
15
15
  */
16
16
 
17
17
  import { describe, expect, it } from 'vitest';
18
- import { sanitizeHtml } from '../../../../src/lib/utils/purify.js';
18
+ import { sanitizeHtml } from '../../../../../src/lib/utils/purify.js';
19
19
 
20
20
  describe('sanitizeHtml', () => {
21
21
  it('removes dangerous tags like <script>', async () => {
@@ -26,10 +26,7 @@ export default defineConfig({
26
26
  name: 'client',
27
27
  environment: 'jsdom',
28
28
  clearMocks: true,
29
- include: [
30
- 'tests/unit/**/*.test.{js,mjs,svelte}',
31
- 'tests/internal/**/*.test.{js,mjs,svelte}',
32
- ],
29
+ include: ['tests/unit/client/**/*.test.{js,mjs,svelte}'],
33
30
  exclude: [],
34
31
  setupFiles: ['./vitest-setup-client.js'],
35
32
  reporters: ['default', 'json'],
@@ -23,7 +23,7 @@ export default defineConfig({
23
23
  test: {
24
24
  name: 'server',
25
25
  environment: 'node',
26
- include: ['tests/unit/**/*.test.{js,mjs}'],
26
+ include: ['tests/unit/server/**/*.test.{js,mjs}'],
27
27
  exclude: ['tests/unit/**/*.svelte.test.{js,mjs}'],
28
28
  reporters: ['default', 'json'],
29
29
  testTimeout: 10000,
@@ -1,49 +0,0 @@
1
- /* ==========================================================================
2
- tests/unit/utm.test.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
- import { appendUTM } from '$lib/utils/utm.js';
10
- import { afterEach, describe, expect, it } from 'vitest';
11
-
12
- describe('appendUTM', () => {
13
- const originalWindow = globalThis.window;
14
-
15
- afterEach(() => {
16
- globalThis.window = originalWindow;
17
- });
18
-
19
- it('should return null when not in a browser environment', () => {
20
- // @ts-expect-error – simulating SSR
21
- delete globalThis.window;
22
-
23
- const url = 'https://example.com';
24
- const result = appendUTM(url);
25
- expect(result).toBe(null);
26
- });
27
-
28
- it('should return URL with utm_source appended', () => {
29
- globalThis.window = {
30
- // @ts-expect-error – mock minimal window for test
31
- location: { search: '?utm_source=linkedin' },
32
- };
33
-
34
- const url = 'https://example.com';
35
- const result = appendUTM(url);
36
- expect(result).toBe('https://example.com?utm_source=linkedin');
37
- });
38
-
39
- it('should return original URL if no utm_source is present', () => {
40
- globalThis.window = {
41
- // @ts-expect-error – mock minimal window for test
42
- location: { search: '' },
43
- };
44
-
45
- const url = 'https://example.com';
46
- const result = appendUTM(url);
47
- expect(result).toBe('https://example.com');
48
- });
49
- });