swup 4.3.4 → 4.4.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/dist/Swup.cjs +1 -1
- package/dist/Swup.cjs.map +1 -1
- package/dist/Swup.modern.js +1 -1
- package/dist/Swup.modern.js.map +1 -1
- package/dist/Swup.module.js +1 -1
- package/dist/Swup.module.js.map +1 -1
- package/dist/Swup.umd.js +1 -1
- package/dist/Swup.umd.js.map +1 -1
- package/dist/types/Swup.d.ts +2 -0
- package/dist/types/modules/Hooks.d.ts +5 -0
- package/dist/types/modules/fetchPage.d.ts +8 -2
- package/package.json +12 -17
- package/src/Swup.ts +4 -1
- package/src/modules/Hooks.ts +5 -0
- package/src/modules/fetchPage.ts +47 -8
- package/src/modules/navigate.ts +22 -15
- package/dist/types/__test__/index.test.d.ts +0 -1
- package/dist/types/helpers/__test__/matchPath.test.d.ts +0 -1
- package/dist/types/modules/__test__/cache.test.d.ts +0 -1
- package/dist/types/modules/__test__/delegateEvent.test.d.ts +0 -1
- package/dist/types/modules/__test__/hooks.test.d.ts +0 -1
- package/dist/types/modules/__test__/plugins.test.d.ts +0 -1
- package/dist/types/modules/__test__/replaceContent.test.d.ts +0 -1
- package/dist/types/modules/__test__/visit.test.d.ts +0 -1
- package/src/__test__/index.test.ts +0 -83
- package/src/helpers/__test__/matchPath.test.ts +0 -54
- package/src/modules/__test__/cache.test.ts +0 -159
- package/src/modules/__test__/delegateEvent.test.ts +0 -36
- package/src/modules/__test__/hooks.test.ts +0 -319
- package/src/modules/__test__/plugins.test.ts +0 -89
- package/src/modules/__test__/replaceContent.test.ts +0 -91
- package/src/modules/__test__/visit.test.ts +0 -92
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "swup",
|
|
3
3
|
"amdName": "Swup",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.4.0",
|
|
5
5
|
"description": "Versatile and extensible page transition library for server-rendered websites",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"source": "./src/Swup.ts",
|
|
@@ -35,17 +35,14 @@
|
|
|
35
35
|
"postinstall": "opencollective-postinstall || true",
|
|
36
36
|
"test": "npm run test:unit && npm run test:e2e",
|
|
37
37
|
"test:ci": "npm run test:unit && npm run test:e2e:ci",
|
|
38
|
-
"test:unit": "vitest run --config ./
|
|
39
|
-
"test:unit:watch": "vitest --config ./
|
|
40
|
-
"test:e2e": "
|
|
41
|
-
"test:e2e:
|
|
42
|
-
"test:e2e:
|
|
43
|
-
"test:e2e:instrument": "nyc instrument --compact=false dist
|
|
44
|
-
"test:e2e:
|
|
45
|
-
"test:e2e:start": "npm run test:e2e:instrument && npm run test:e2e:
|
|
46
|
-
"cy:run": "cypress run",
|
|
47
|
-
"cy:run:record": "cypress run --record",
|
|
48
|
-
"cy:open": "cypress open",
|
|
38
|
+
"test:unit": "vitest run --config ./tests/config/vitest.config.ts",
|
|
39
|
+
"test:unit:watch": "vitest --config ./tests/config/vitest.config.ts",
|
|
40
|
+
"test:e2e": "npx playwright test --config ./tests/config/playwright.config.ts",
|
|
41
|
+
"test:e2e:dev": "npx playwright test --ui --config ./tests/config/playwright.config.ts",
|
|
42
|
+
"test:e2e:install": "npx playwright install --with-deps",
|
|
43
|
+
"test:e2e:instrument": "nyc instrument --compact=false dist tests/fixtures/dist",
|
|
44
|
+
"test:e2e:serve": "npx serve -n -S -L -p 8274 --config ./tests/config/serve.json",
|
|
45
|
+
"test:e2e:start": "npm run test:e2e:instrument && npm run test:e2e:serve",
|
|
49
46
|
"prepare": "husky install"
|
|
50
47
|
},
|
|
51
48
|
"author": "Georgy Marchuk",
|
|
@@ -67,25 +64,23 @@
|
|
|
67
64
|
},
|
|
68
65
|
"devDependencies": {
|
|
69
66
|
"@babel/preset-typescript": "^7.18.6",
|
|
70
|
-
"@
|
|
67
|
+
"@playwright/test": "^1.37.1",
|
|
71
68
|
"@swup/browserslist-config": "^1.0.0",
|
|
72
69
|
"@swup/prettier-config": "^1.0.0",
|
|
73
70
|
"@types/jsdom": "^21.1.1",
|
|
74
71
|
"@typescript-eslint/eslint-plugin": "^6.3.0",
|
|
75
72
|
"@typescript-eslint/parser": "^6.3.0",
|
|
76
|
-
"cypress": "^12.3.0",
|
|
77
73
|
"eslint": "^8.46.0",
|
|
78
74
|
"eslint-config-prettier": "^9.0.0",
|
|
79
75
|
"eslint-plugin-prettier": "^4.2.1",
|
|
80
|
-
"http-server": "^14.1.1",
|
|
81
76
|
"husky": "^8.0.0",
|
|
82
77
|
"istanbul-lib-coverage": "^3.2.0",
|
|
83
78
|
"jsdom": "^22.1.0",
|
|
84
79
|
"microbundle": "^0.15.0",
|
|
85
80
|
"nyc": "^15.1.0",
|
|
86
81
|
"prettier": "^2.8.2",
|
|
87
|
-
"
|
|
88
|
-
"vitest": "^0.
|
|
82
|
+
"serve": "^14.2.1",
|
|
83
|
+
"vitest": "^0.34.3"
|
|
89
84
|
},
|
|
90
85
|
"collective": {
|
|
91
86
|
"type": "opencollective",
|
package/src/Swup.ts
CHANGED
|
@@ -49,6 +49,8 @@ export type Options = {
|
|
|
49
49
|
resolveUrl: (url: string) => string;
|
|
50
50
|
/** Callback for telling swup to ignore certain popstate events. */
|
|
51
51
|
skipPopStateHandling: (event: PopStateEvent) => boolean;
|
|
52
|
+
/** Request timeout in milliseconds. */
|
|
53
|
+
timeout: number;
|
|
52
54
|
};
|
|
53
55
|
|
|
54
56
|
const defaults: Options = {
|
|
@@ -66,7 +68,8 @@ const defaults: Options = {
|
|
|
66
68
|
'X-Requested-With': 'swup',
|
|
67
69
|
'Accept': 'text/html, application/xhtml+xml'
|
|
68
70
|
},
|
|
69
|
-
skipPopStateHandling: (event) => (event.state as HistoryState)?.source !== 'swup'
|
|
71
|
+
skipPopStateHandling: (event) => (event.state as HistoryState)?.source !== 'swup',
|
|
72
|
+
timeout: 0
|
|
70
73
|
};
|
|
71
74
|
|
|
72
75
|
/** Swup page transition library. */
|
package/src/modules/Hooks.ts
CHANGED
|
@@ -21,6 +21,7 @@ export interface HookDefinitions {
|
|
|
21
21
|
'disable': undefined;
|
|
22
22
|
'fetch:request': { url: string; options: FetchOptions };
|
|
23
23
|
'fetch:error': { url: string; status: number; response: Response };
|
|
24
|
+
'fetch:timeout': { url: string };
|
|
24
25
|
'history:popstate': { event: PopStateEvent };
|
|
25
26
|
'link:click': { el: HTMLAnchorElement; event: DelegateEvent<MouseEvent> };
|
|
26
27
|
'link:self': undefined;
|
|
@@ -31,6 +32,7 @@ export interface HookDefinitions {
|
|
|
31
32
|
'scroll:top': { options: ScrollIntoViewOptions };
|
|
32
33
|
'scroll:anchor': { hash: string; options: ScrollIntoViewOptions };
|
|
33
34
|
'visit:start': undefined;
|
|
35
|
+
'visit:transition': undefined;
|
|
34
36
|
'visit:end': undefined;
|
|
35
37
|
}
|
|
36
38
|
|
|
@@ -40,6 +42,7 @@ export interface HookReturnValues {
|
|
|
40
42
|
'page:load': Promise<PageData>;
|
|
41
43
|
'scroll:top': boolean;
|
|
42
44
|
'scroll:anchor': boolean;
|
|
45
|
+
'visit:transition': Promise<boolean>;
|
|
43
46
|
}
|
|
44
47
|
|
|
45
48
|
export type HookArguments<T extends HookName> = HookDefinitions[T];
|
|
@@ -131,6 +134,7 @@ export class Hooks {
|
|
|
131
134
|
'disable',
|
|
132
135
|
'fetch:request',
|
|
133
136
|
'fetch:error',
|
|
137
|
+
'fetch:timeout',
|
|
134
138
|
'history:popstate',
|
|
135
139
|
'link:click',
|
|
136
140
|
'link:self',
|
|
@@ -141,6 +145,7 @@ export class Hooks {
|
|
|
141
145
|
'scroll:top',
|
|
142
146
|
'scroll:anchor',
|
|
143
147
|
'visit:start',
|
|
148
|
+
'visit:transition',
|
|
144
149
|
'visit:end'
|
|
145
150
|
];
|
|
146
151
|
|
package/src/modules/fetchPage.ts
CHANGED
|
@@ -15,16 +15,25 @@ export interface FetchOptions extends Omit<RequestInit, 'cache'> {
|
|
|
15
15
|
method?: 'GET' | 'POST';
|
|
16
16
|
/** The body of the request: raw string, form data object or URL params. */
|
|
17
17
|
body?: string | FormData | URLSearchParams;
|
|
18
|
+
/** The request timeout in milliseconds. */
|
|
19
|
+
timeout?: number;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
export class FetchError extends Error {
|
|
21
23
|
url: string;
|
|
22
|
-
status
|
|
23
|
-
|
|
24
|
+
status?: number;
|
|
25
|
+
aborted: boolean;
|
|
26
|
+
timedOut: boolean;
|
|
27
|
+
constructor(
|
|
28
|
+
message: string,
|
|
29
|
+
details: { url: string; status?: number; aborted?: boolean; timedOut?: boolean }
|
|
30
|
+
) {
|
|
24
31
|
super(message);
|
|
25
32
|
this.name = 'FetchError';
|
|
26
33
|
this.url = details.url;
|
|
27
34
|
this.status = details.status;
|
|
35
|
+
this.aborted = details.aborted || false;
|
|
36
|
+
this.timedOut = details.timedOut || false;
|
|
28
37
|
}
|
|
29
38
|
}
|
|
30
39
|
|
|
@@ -39,14 +48,44 @@ export async function fetchPage(
|
|
|
39
48
|
url = Location.fromUrl(url).url;
|
|
40
49
|
|
|
41
50
|
const headers = { ...this.options.requestHeaders, ...options.headers };
|
|
42
|
-
|
|
51
|
+
const timeout = options.timeout ?? this.options.timeout;
|
|
52
|
+
const controller = new AbortController();
|
|
53
|
+
const { signal } = controller;
|
|
54
|
+
options = { ...options, headers, signal };
|
|
55
|
+
|
|
56
|
+
let timedOut = false;
|
|
57
|
+
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
58
|
+
if (timeout && timeout > 0) {
|
|
59
|
+
timeoutId = setTimeout(() => {
|
|
60
|
+
timedOut = true;
|
|
61
|
+
controller.abort('timeout');
|
|
62
|
+
}, timeout);
|
|
63
|
+
}
|
|
43
64
|
|
|
44
65
|
// Allow hooking before this and returning a custom response-like object (e.g. custom fetch implementation)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
66
|
+
let response: Response;
|
|
67
|
+
try {
|
|
68
|
+
response = await this.hooks.call(
|
|
69
|
+
'fetch:request',
|
|
70
|
+
{ url, options },
|
|
71
|
+
(visit, { url, options }) => fetch(url, options)
|
|
72
|
+
);
|
|
73
|
+
if (timeoutId) {
|
|
74
|
+
clearTimeout(timeoutId);
|
|
75
|
+
}
|
|
76
|
+
} catch (error) {
|
|
77
|
+
if (timedOut) {
|
|
78
|
+
this.hooks.call('fetch:timeout', { url });
|
|
79
|
+
throw new FetchError(`Request timed out: ${url}`, { url, timedOut });
|
|
80
|
+
}
|
|
81
|
+
if ((error as Error)?.name === 'AbortError' || signal.aborted) {
|
|
82
|
+
throw new FetchError(`Request aborted: ${url}`, {
|
|
83
|
+
url: url,
|
|
84
|
+
aborted: true
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
50
89
|
|
|
51
90
|
const { status, url: responseUrl } = response;
|
|
52
91
|
const html = await response.text();
|
package/src/modules/navigate.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Swup from '../Swup.js';
|
|
2
2
|
import { createHistoryRecord, updateHistoryRecord, getCurrentUrl, Location } from '../helpers.js';
|
|
3
|
-
import { FetchOptions, PageData } from './fetchPage.js';
|
|
3
|
+
import { FetchError, FetchOptions, PageData } from './fetchPage.js';
|
|
4
4
|
import { VisitInitOptions } from './Visit.js';
|
|
5
5
|
|
|
6
6
|
export type HistoryAction = 'push' | 'replace';
|
|
@@ -137,20 +137,27 @@ export async function performNavigation(
|
|
|
137
137
|
visit.to.html = html;
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
|
|
140
|
+
// perform the actual transition: animate and replace content
|
|
141
|
+
await this.hooks.call('visit:transition', undefined, async (visit) => {
|
|
142
|
+
// Start leave animation
|
|
143
|
+
const animationPromise = this.animatePageOut();
|
|
143
144
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
145
|
+
// Wait for page to load and leave animation to finish
|
|
146
|
+
const [page] = await Promise.all([pagePromise, animationPromise]);
|
|
147
|
+
|
|
148
|
+
// Abort if another visit was started in the meantime
|
|
149
|
+
if (visit.id !== this.visit.id) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
148
152
|
|
|
149
|
-
|
|
150
|
-
|
|
153
|
+
// Render page: replace content and scroll to top/fragment
|
|
154
|
+
await this.renderPage(page);
|
|
151
155
|
|
|
152
|
-
|
|
153
|
-
|
|
156
|
+
// Wait for enter animation
|
|
157
|
+
await this.animatePageIn();
|
|
158
|
+
|
|
159
|
+
return true;
|
|
160
|
+
});
|
|
154
161
|
|
|
155
162
|
// Finalize visit
|
|
156
163
|
await this.hooks.call('visit:end', undefined, () => this.classes.clear());
|
|
@@ -159,9 +166,9 @@ export async function performNavigation(
|
|
|
159
166
|
// if (visit.to && this.isSameResolvedUrl(visit.to.url, requestedUrl)) {
|
|
160
167
|
// this.visit = this.createVisit({ to: undefined });
|
|
161
168
|
// }
|
|
162
|
-
} catch (error
|
|
163
|
-
// Return early if error is undefined
|
|
164
|
-
if (!error) {
|
|
169
|
+
} catch (error) {
|
|
170
|
+
// Return early if error is undefined or signals an aborted request
|
|
171
|
+
if (!error || (error as FetchError)?.aborted) {
|
|
165
172
|
return;
|
|
166
173
|
}
|
|
167
174
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { DelegateEvent } from 'delegate-it';
|
|
2
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
-
|
|
4
|
-
import pckg from '../../package.json';
|
|
5
|
-
import Swup, { Options, Plugin } from '../index.js';
|
|
6
|
-
import * as SwupTS from '../Swup.js';
|
|
7
|
-
|
|
8
|
-
const baseUrl = window.location.origin;
|
|
9
|
-
|
|
10
|
-
describe('Exports', () => {
|
|
11
|
-
it('should export Swup and Options/Plugin types', () => {
|
|
12
|
-
class SwupPlugin implements Plugin {
|
|
13
|
-
name = 'SwupPlugin';
|
|
14
|
-
isSwupPlugin = true as const;
|
|
15
|
-
mount = () => {};
|
|
16
|
-
unmount = () => {};
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const options: Partial<Options> = {
|
|
20
|
-
animateHistoryBrowsing: false,
|
|
21
|
-
animationSelector: '[class*="transition-"]',
|
|
22
|
-
cache: true,
|
|
23
|
-
containers: ['#swup'],
|
|
24
|
-
ignoreVisit: (url, { el } = {}) => !!el?.closest('[data-no-swup]'),
|
|
25
|
-
linkSelector: 'a[href]',
|
|
26
|
-
plugins: [new SwupPlugin()],
|
|
27
|
-
resolveUrl: (url) => url,
|
|
28
|
-
requestHeaders: {
|
|
29
|
-
'X-Requested-With': 'swup',
|
|
30
|
-
'Accept': 'text/html, application/xhtml+xml'
|
|
31
|
-
},
|
|
32
|
-
skipPopStateHandling: (event) => event.state?.source !== 'swup'
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const swup = new Swup(options);
|
|
36
|
-
expect(swup).toBeInstanceOf(Swup);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('should define a version', () => {
|
|
40
|
-
const swup = new Swup();
|
|
41
|
-
expect(swup.version).not.toBeUndefined();
|
|
42
|
-
expect(swup.version).toEqual(pckg.version);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('UMD compatibility: Swup.ts should only have a default export', () => {
|
|
46
|
-
expect(Object.keys(SwupTS)).toEqual(['default']);
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
describe('ignoreVisit', () => {
|
|
51
|
-
it('should be called with relative URL', () => {
|
|
52
|
-
const ignoreVisit = vi.fn(() => true);
|
|
53
|
-
const swup = new Swup({ ignoreVisit });
|
|
54
|
-
swup.shouldIgnoreVisit(`${baseUrl}/path/?query#hash`);
|
|
55
|
-
|
|
56
|
-
expect(ignoreVisit.mock.calls).toHaveLength(1);
|
|
57
|
-
expect((ignoreVisit.mock.lastCall as any)[0]).toEqual('/path/?query#hash');
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('should have access to element and event params', () => {
|
|
61
|
-
const el = document.createElement('a');
|
|
62
|
-
el.href = `${baseUrl}/path/?query#hash`;
|
|
63
|
-
const event = new MouseEvent('click') as DelegateEvent<MouseEvent>;
|
|
64
|
-
event.delegateTarget = el;
|
|
65
|
-
|
|
66
|
-
const ignoreVisit = vi.fn(() => true);
|
|
67
|
-
const swup = new Swup({ ignoreVisit });
|
|
68
|
-
swup.navigate(el.href, {}, { el, event });
|
|
69
|
-
|
|
70
|
-
expect(ignoreVisit.mock.calls).toHaveLength(1);
|
|
71
|
-
expect((ignoreVisit.mock.lastCall as any)[1]).toEqual(
|
|
72
|
-
expect.objectContaining({ el, event })
|
|
73
|
-
);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('should be called from visit method', () => {
|
|
77
|
-
const ignoreVisit = vi.fn(() => true);
|
|
78
|
-
const swup = new Swup({ ignoreVisit });
|
|
79
|
-
swup.navigate('/path/');
|
|
80
|
-
|
|
81
|
-
expect(ignoreVisit.mock.calls).toHaveLength(1);
|
|
82
|
-
});
|
|
83
|
-
});
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { matchPath } from '../../index.js';
|
|
3
|
-
import { pathToRegexp } from 'path-to-regexp';
|
|
4
|
-
|
|
5
|
-
describe('matchPath', () => {
|
|
6
|
-
it('should return false if not matching', () => {
|
|
7
|
-
const urlMatch = matchPath('/users/:user');
|
|
8
|
-
const match = urlMatch('/posts/');
|
|
9
|
-
expect(match).toBe(false);
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
it('should return an object if matching', () => {
|
|
13
|
-
const urlMatch = matchPath('/users/:user');
|
|
14
|
-
const match = urlMatch('/users/bob');
|
|
15
|
-
expect(match).toEqual({
|
|
16
|
-
path: '/users/bob',
|
|
17
|
-
index: 0,
|
|
18
|
-
params: { user: 'bob' }
|
|
19
|
-
});
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('should work with primitive strings', () => {
|
|
23
|
-
const urlMatch = matchPath<{ user: string }>('/users/:user');
|
|
24
|
-
const match = urlMatch('/users/bob');
|
|
25
|
-
const params = !match ? false : match.params;
|
|
26
|
-
expect(params).toEqual({ user: 'bob' });
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('should work with an array of paths', () => {
|
|
30
|
-
const urlMatch = matchPath<{ user: string }>(['/users/', '/users/:user']);
|
|
31
|
-
|
|
32
|
-
const { params: withParams } = urlMatch('/users/bob') || {};
|
|
33
|
-
expect(withParams).toEqual({ user: 'bob' });
|
|
34
|
-
|
|
35
|
-
const { params: withoutParams } = urlMatch('/users/') || {};
|
|
36
|
-
expect(withoutParams).toEqual({});
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* When passing a regex to `match`, the params in the response are sorted by appearance.
|
|
41
|
-
* Only helpful for falsy/truthy detection
|
|
42
|
-
*/
|
|
43
|
-
it('should work with regex', () => {
|
|
44
|
-
const re = pathToRegexp('/users/:user');
|
|
45
|
-
const urlMatch = matchPath(re);
|
|
46
|
-
const { params } = urlMatch('/users/bob') || {};
|
|
47
|
-
expect(params).toEqual({ '0': 'bob' });
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('should throw with malformed paths', () => {
|
|
51
|
-
// prettier-ignore
|
|
52
|
-
expect(() => matchPath('/\?user=:user')).toThrowError('[swup] Error parsing path');
|
|
53
|
-
});
|
|
54
|
-
});
|
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import Swup from '../../Swup.js';
|
|
3
|
-
import { Cache, CacheData } from '../Cache.js';
|
|
4
|
-
import { Visit } from '../Visit.js';
|
|
5
|
-
|
|
6
|
-
interface CacheTtlData {
|
|
7
|
-
ttl: number;
|
|
8
|
-
created: number;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
interface CacheIndexData {
|
|
12
|
-
index: number;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
interface AugmentedCacheData extends CacheData, CacheTtlData, CacheIndexData {}
|
|
16
|
-
|
|
17
|
-
const swup = new Swup();
|
|
18
|
-
const visit = swup.visit;
|
|
19
|
-
const cache = new Cache(swup);
|
|
20
|
-
|
|
21
|
-
const page1 = { url: '/page-1', html: '1' };
|
|
22
|
-
const page2 = { url: '/page-2', html: '2' };
|
|
23
|
-
const page3 = { url: '/page-3', html: '3' };
|
|
24
|
-
|
|
25
|
-
describe('Cache', () => {
|
|
26
|
-
beforeEach(() => {
|
|
27
|
-
cache.clear();
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('should be empty', () => {
|
|
31
|
-
expect(cache.size).toBe(0);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('should append pages', () => {
|
|
35
|
-
cache.set(page1.url, page1);
|
|
36
|
-
expect(cache.size).toBe(1);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('should have pages', () => {
|
|
40
|
-
cache.set(page1.url, page1);
|
|
41
|
-
expect(cache.has(page1.url)).toBe(true);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('should get pages', () => {
|
|
45
|
-
cache.set(page1.url, page1);
|
|
46
|
-
expect(cache.get(page1.url)).toEqual(page1);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('should delete pages', () => {
|
|
50
|
-
cache.set(page1.url, page1);
|
|
51
|
-
expect(cache.has(page1.url)).toBe(true);
|
|
52
|
-
cache.delete(page1.url);
|
|
53
|
-
expect(cache.has(page1.url)).toBe(false);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it('should clear', () => {
|
|
57
|
-
cache.set(page1.url, page1);
|
|
58
|
-
expect(cache.size).toBe(1);
|
|
59
|
-
cache.clear();
|
|
60
|
-
expect(cache.size).toBe(0);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('should overwrite identical pages', () => {
|
|
64
|
-
cache.set(page1.url, page1);
|
|
65
|
-
expect(cache.size).toBe(1);
|
|
66
|
-
cache.set(page1.url, page1);
|
|
67
|
-
expect(cache.size).toBe(1);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('should not overwrite different pages', () => {
|
|
71
|
-
cache.set(page1.url, page1);
|
|
72
|
-
expect(cache.size).toBe(1);
|
|
73
|
-
cache.set(page2.url, page2);
|
|
74
|
-
expect(cache.size).toBe(2);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('should trigger a hook on set', () => {
|
|
78
|
-
const handler = vi.fn();
|
|
79
|
-
|
|
80
|
-
swup.hooks.on('cache:set', handler);
|
|
81
|
-
|
|
82
|
-
cache.set(page1.url, page1);
|
|
83
|
-
|
|
84
|
-
expect(handler).toBeCalledTimes(1);
|
|
85
|
-
expect(handler).toBeCalledWith(visit, { page: page1 }, undefined);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('should allow augmenting cache entries on save', () => {
|
|
89
|
-
const now = Date.now();
|
|
90
|
-
|
|
91
|
-
swup.hooks.on('cache:set', (_, { page }) => {
|
|
92
|
-
const ttl: CacheTtlData = { ttl: 1000, created: now };
|
|
93
|
-
cache.update(page.url, ttl);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
cache.set('/page', { url: '/page', html: '' });
|
|
97
|
-
|
|
98
|
-
const page = cache.get('/page');
|
|
99
|
-
|
|
100
|
-
expect(page).toEqual({ url: '/page', html: '', ttl: 1000, created: now });
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('should allow manual pruning', () => {
|
|
104
|
-
swup.hooks.on('cache:set', (_, { page }) => {
|
|
105
|
-
cache.update(page.url, { index: cache.size });
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
cache.set(page1.url, page1);
|
|
109
|
-
cache.set(page2.url, page2);
|
|
110
|
-
cache.set(page3.url, page3);
|
|
111
|
-
|
|
112
|
-
cache.prune((url, page) => (page as AugmentedCacheData).index > 2);
|
|
113
|
-
|
|
114
|
-
expect(cache.size).toBe(2);
|
|
115
|
-
expect(cache.has(page1.url)).toBe(true);
|
|
116
|
-
expect(cache.has(page2.url)).toBe(true);
|
|
117
|
-
expect(cache.has(page3.url)).toBe(false);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it('should return a copy from cache.get()', () => {
|
|
121
|
-
cache.set(page1.url, page1);
|
|
122
|
-
const page = cache.get(page1.url);
|
|
123
|
-
page!.html = 'new';
|
|
124
|
-
expect(cache.get(page1.url)?.html).toEqual(page1.html);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it('should return a new Map with shallow copies from cache.all', () => {
|
|
128
|
-
cache.set(page1.url, page1);
|
|
129
|
-
cache.set(page2.url, page2);
|
|
130
|
-
|
|
131
|
-
const all = cache.all;
|
|
132
|
-
all.get(page1.url)!.html = 'new';
|
|
133
|
-
|
|
134
|
-
expect(cache.get(page1.url)?.html).toEqual(page1.html);
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
describe('Types', () => {
|
|
139
|
-
it('error when necessary', async () => {
|
|
140
|
-
const swup = new Swup();
|
|
141
|
-
const cache = new Cache(swup);
|
|
142
|
-
|
|
143
|
-
// @ts-expect-no-error
|
|
144
|
-
swup.hooks.on('history:popstate', (visit: Visit, { event: PopStateEvent }) => {});
|
|
145
|
-
// @ts-expect-no-error
|
|
146
|
-
await swup.hooks.call('history:popstate', { event: new PopStateEvent('') });
|
|
147
|
-
|
|
148
|
-
try {
|
|
149
|
-
// @ts-expect-error
|
|
150
|
-
cache.set();
|
|
151
|
-
// @ts-expect-error
|
|
152
|
-
cache.set(url);
|
|
153
|
-
// @ts-expect-error
|
|
154
|
-
cache.set(url, {});
|
|
155
|
-
// @ts-expect-error
|
|
156
|
-
cache.set({ url: '/test' });
|
|
157
|
-
} catch (error) {}
|
|
158
|
-
});
|
|
159
|
-
});
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { DelegateEvent } from 'delegate-it';
|
|
2
|
-
import { describe, it } from 'vitest';
|
|
3
|
-
|
|
4
|
-
import { delegateEvent } from '../../helpers/delegateEvent.js';
|
|
5
|
-
|
|
6
|
-
describe('delegateEvent', () => {
|
|
7
|
-
it('should return correct types', () => {
|
|
8
|
-
delegateEvent('form', 'submit', (event) => {});
|
|
9
|
-
|
|
10
|
-
// @ts-expect-no-error
|
|
11
|
-
delegateEvent('form', 'submit', (event: SubmitEvent) => {});
|
|
12
|
-
// @ts-expect-error
|
|
13
|
-
delegateEvent('form', 'submit', (event: MouseEvent) => {});
|
|
14
|
-
|
|
15
|
-
// @ts-expect-no-error
|
|
16
|
-
delegateEvent('form', 'submit', (event: DelegateEvent<SubmitEvent>) => {});
|
|
17
|
-
// @ts-expect-error
|
|
18
|
-
delegateEvent('form', 'submit', (event: DelegateEvent<MouseEvent>) => {});
|
|
19
|
-
|
|
20
|
-
// @ts-expect-no-error
|
|
21
|
-
delegateEvent('form', 'submit', (event: DelegateEvent<SubmitEvent, HTMLFormElement>) => {});
|
|
22
|
-
delegateEvent(
|
|
23
|
-
'form',
|
|
24
|
-
'submit',
|
|
25
|
-
// @ts-expect-error
|
|
26
|
-
(event: DelegateEvent<MouseEvent, HTMLAnchorElement>) => {}
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
delegateEvent('form', 'submit', (event) => {
|
|
30
|
-
// @ts-expect-no-error
|
|
31
|
-
const el: HTMLFormElement = event.delegateTarget;
|
|
32
|
-
});
|
|
33
|
-
// @ts-expect-error
|
|
34
|
-
delegateEvent('form', 'submit', (event: MouseEvent) => {});
|
|
35
|
-
});
|
|
36
|
-
});
|