@antelopejs/dms 0.5.5 → 0.5.6
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 +14 -0
- package/dist/implementations/dms/dev-reload.d.ts +1 -0
- package/dist/implementations/dms/dev-reload.js +9 -0
- package/dist/implementations/dms/dev-reload.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/test/unit/implementations/dms/dev-reload.test.js +41 -0
- package/dist/test/unit/implementations/dms/dev-reload.test.js.map +1 -1
- package/frontend-vue/layers/dms-layout/app/plugins/dms-dev-reload.client.ts +8 -14
- package/frontend-vue/layers/dms-layout/app/utils/dev-reload-stream.ts +80 -0
- package/frontend-vue/layers/dms-ui/app/components/tab/Tab.vue +1 -1
- package/frontend-vue/tests/dev-reload-stream.test.ts +139 -0
- package/frontend-vue/tests/tab-slot-spacing.test.ts +21 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v0.5.6
|
|
4
|
+
|
|
5
|
+
[compare changes](https://github.com/AntelopeJS/dms/compare/v0.5.5...v0.5.6)
|
|
6
|
+
|
|
7
|
+
### 🩹 Fixes
|
|
8
|
+
|
|
9
|
+
- **tab:** Space the blocks of a tab like the page spaces its own ([#98](https://github.com/AntelopeJS/dms/pull/98))
|
|
10
|
+
- **dev-reload:** End the reload streams when the module stops ([#125](https://github.com/AntelopeJS/dms/pull/125))
|
|
11
|
+
|
|
12
|
+
### ❤️ Contributors
|
|
13
|
+
|
|
14
|
+
- Antony Rizzitelli <rizzitelli.antony@pm.me>
|
|
15
|
+
- Glastis ([@Glastis](http://github.com/Glastis))
|
|
16
|
+
|
|
3
17
|
## v0.5.5
|
|
4
18
|
|
|
5
19
|
[compare changes](https://github.com/AntelopeJS/dms/compare/v0.5.4...v0.5.5)
|
|
@@ -3,6 +3,7 @@ type SlugProvider = () => string[];
|
|
|
3
3
|
export declare function setSlugProvider(provider: SlugProvider): void;
|
|
4
4
|
export declare function scheduleBroadcast(): void;
|
|
5
5
|
export declare function cancelScheduledBroadcast(): void;
|
|
6
|
+
export declare function closeDevReloadStreams(): void;
|
|
6
7
|
declare const DevReloadController_base: import("@antelopejs/interface-api").ControllerClass<object>;
|
|
7
8
|
export declare class DevReloadController extends DevReloadController_base {
|
|
8
9
|
reload(out: PassThrough): void;
|
|
@@ -16,6 +16,7 @@ exports.DevReloadController = void 0;
|
|
|
16
16
|
exports.setSlugProvider = setSlugProvider;
|
|
17
17
|
exports.scheduleBroadcast = scheduleBroadcast;
|
|
18
18
|
exports.cancelScheduledBroadcast = cancelScheduledBroadcast;
|
|
19
|
+
exports.closeDevReloadStreams = closeDevReloadStreams;
|
|
19
20
|
const interface_api_1 = require("@antelopejs/interface-api");
|
|
20
21
|
const BROADCAST_DEBOUNCE_MS = 250;
|
|
21
22
|
const SSE_CONTENT_TYPE = "text/event-stream";
|
|
@@ -62,6 +63,14 @@ function cancelScheduledBroadcast() {
|
|
|
62
63
|
clearTimeout(broadcastTimer);
|
|
63
64
|
broadcastTimer = null;
|
|
64
65
|
}
|
|
66
|
+
function closeDevReloadStreams() {
|
|
67
|
+
cancelScheduledBroadcast();
|
|
68
|
+
const streams = [...clients];
|
|
69
|
+
clients.clear();
|
|
70
|
+
for (const stream of streams) {
|
|
71
|
+
stream.end();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
65
74
|
function registerClient(stream) {
|
|
66
75
|
clients.add(stream);
|
|
67
76
|
writeToClient(stream, SSE_HELLO_PAYLOAD);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-reload.js","sourceRoot":"","sources":["../../../src/implementations/dms/dev-reload.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAiBA,0CAEC;AAyCD,8CAIC;AAGD,4DAIC;
|
|
1
|
+
{"version":3,"file":"dev-reload.js","sourceRoot":"","sources":["../../../src/implementations/dms/dev-reload.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -167,6 +167,7 @@ function cancelDeferredNotifications() {
|
|
|
167
167
|
}
|
|
168
168
|
function destroy() {
|
|
169
169
|
cancelDeferredNotifications();
|
|
170
|
+
(0, dev_reload_1.closeDevReloadStreams)();
|
|
170
171
|
}
|
|
171
172
|
async function start() {
|
|
172
173
|
await (0, realtime_1.configureRealtime)(globalRealtimeConfig);
|
|
@@ -196,6 +197,7 @@ async function stop() {
|
|
|
196
197
|
cronTasks = [];
|
|
197
198
|
await (0, realtime_1.stopRealtime)();
|
|
198
199
|
cancelDeferredNotifications();
|
|
200
|
+
(0, dev_reload_1.closeDevReloadStreams)();
|
|
199
201
|
}
|
|
200
202
|
}
|
|
201
203
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,6 +12,12 @@ function connect() {
|
|
|
12
12
|
function read(stream) {
|
|
13
13
|
return String(stream.read() ?? "");
|
|
14
14
|
}
|
|
15
|
+
function ended(stream) {
|
|
16
|
+
return new Promise((resolve) => {
|
|
17
|
+
stream.once("end", resolve);
|
|
18
|
+
stream.resume();
|
|
19
|
+
});
|
|
20
|
+
}
|
|
15
21
|
function settle() {
|
|
16
22
|
return new Promise((resolve) => setTimeout(resolve, SETTLE_MS));
|
|
17
23
|
}
|
|
@@ -55,5 +61,40 @@ describe("[unit] implementations/dms/dev-reload", () => {
|
|
|
55
61
|
await settle();
|
|
56
62
|
(0, chai_1.expect)(read(stream)).to.equal("");
|
|
57
63
|
});
|
|
64
|
+
describe("closeDevReloadStreams", () => {
|
|
65
|
+
it("ends every open stream", async () => {
|
|
66
|
+
const other = connect();
|
|
67
|
+
(0, dev_reload_1.closeDevReloadStreams)();
|
|
68
|
+
await Promise.all([ended(stream), ended(other)]);
|
|
69
|
+
(0, chai_1.expect)(stream.writableEnded).to.equal(true);
|
|
70
|
+
(0, chai_1.expect)(other.writableEnded).to.equal(true);
|
|
71
|
+
});
|
|
72
|
+
it("drops the broadcast scheduled before the streams closed", async () => {
|
|
73
|
+
(0, dev_reload_1.setSlugProvider)(() => ["/form/form-simple"]);
|
|
74
|
+
(0, dev_reload_1.scheduleBroadcast)();
|
|
75
|
+
(0, dev_reload_1.closeDevReloadStreams)();
|
|
76
|
+
await settle();
|
|
77
|
+
(0, chai_1.expect)(read(stream)).to.equal("");
|
|
78
|
+
});
|
|
79
|
+
it("keeps no ended stream to write to", async () => {
|
|
80
|
+
(0, dev_reload_1.closeDevReloadStreams)();
|
|
81
|
+
const errors = [];
|
|
82
|
+
stream.on("error", (error) => errors.push(error));
|
|
83
|
+
(0, dev_reload_1.setSlugProvider)(() => ["/form/form-simple"]);
|
|
84
|
+
(0, dev_reload_1.scheduleBroadcast)();
|
|
85
|
+
await settle();
|
|
86
|
+
(0, chai_1.expect)(errors).to.deep.equal([]);
|
|
87
|
+
});
|
|
88
|
+
it("serves the streams that connect once the module is back", async () => {
|
|
89
|
+
(0, dev_reload_1.closeDevReloadStreams)();
|
|
90
|
+
const reconnected = connect();
|
|
91
|
+
read(reconnected);
|
|
92
|
+
(0, dev_reload_1.setSlugProvider)(() => ["/form/form-simple"]);
|
|
93
|
+
(0, dev_reload_1.scheduleBroadcast)();
|
|
94
|
+
await settle();
|
|
95
|
+
(0, chai_1.expect)(read(reconnected)).to.equal('event: reload\ndata: {"type":"resync"}\n\n');
|
|
96
|
+
reconnected.end();
|
|
97
|
+
});
|
|
98
|
+
});
|
|
58
99
|
});
|
|
59
100
|
//# sourceMappingURL=dev-reload.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-reload.test.js","sourceRoot":"","sources":["../../../../../src/test/unit/implementations/dms/dev-reload.test.ts"],"names":[],"mappings":";;AAAA,6CAA0C;AAC1C,+BAA8B;AAC9B,
|
|
1
|
+
{"version":3,"file":"dev-reload.test.js","sourceRoot":"","sources":["../../../../../src/test/unit/implementations/dms/dev-reload.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const RELOAD_EVENT_NAME = "reload";
|
|
2
1
|
const RELOAD_ENDPOINT_PATH = "/dms/dev/reload";
|
|
3
2
|
|
|
4
3
|
function resolveBackendBase(): string | null {
|
|
@@ -23,23 +22,18 @@ export default defineDmsPlugin(() => {
|
|
|
23
22
|
|
|
24
23
|
const endpoint = `${backendBase}${RELOAD_ENDPOINT_PATH}`;
|
|
25
24
|
const { coordinator } = useDevReloadHolder();
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
source.addEventListener(RELOAD_EVENT_NAME, () => {
|
|
35
|
-
void coordinator.requestRefresh().catch((err: unknown) => {
|
|
36
|
-
console.error("[dms-dev-reload] Reload handler failed:", err);
|
|
37
|
-
});
|
|
25
|
+
const stream = openDevReloadStream({
|
|
26
|
+
endpoint,
|
|
27
|
+
onReload: () => {
|
|
28
|
+
void coordinator.requestRefresh().catch((err: unknown) => {
|
|
29
|
+
console.error("[dms-dev-reload] Reload handler failed:", err);
|
|
30
|
+
});
|
|
31
|
+
},
|
|
38
32
|
});
|
|
39
33
|
|
|
40
34
|
if (import.meta.hot) {
|
|
41
35
|
import.meta.hot.dispose(() => {
|
|
42
|
-
|
|
36
|
+
stream.close();
|
|
43
37
|
});
|
|
44
38
|
}
|
|
45
39
|
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const RELOAD_EVENT_NAME = "reload";
|
|
2
|
+
const REOPEN_DELAY_MS = 2000;
|
|
3
|
+
|
|
4
|
+
export interface DevReloadStreamOptions {
|
|
5
|
+
endpoint: string;
|
|
6
|
+
onReload: () => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface DevReloadStream {
|
|
10
|
+
close: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface StreamState {
|
|
14
|
+
options: DevReloadStreamOptions;
|
|
15
|
+
source: EventSource | null;
|
|
16
|
+
reopenTimer: ReturnType<typeof setTimeout> | null;
|
|
17
|
+
isConnectionLost: boolean;
|
|
18
|
+
isClosed: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// The browser fires `error` on every failed retry while the backend is down,
|
|
22
|
+
// so only the first one after a working connection is worth a warning.
|
|
23
|
+
function handleError(state: StreamState): void {
|
|
24
|
+
if (!state.isConnectionLost) {
|
|
25
|
+
state.isConnectionLost = true;
|
|
26
|
+
console.warn(
|
|
27
|
+
"[dms-dev-reload] SSE connection lost, waiting for backend...",
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
// EventSource retries a dropped connection by itself, but gives up for good
|
|
31
|
+
// on an answer that is not an event stream (a 404 while the module reloads,
|
|
32
|
+
// a proxy error page): only then does it need a new source.
|
|
33
|
+
if (state.source?.readyState !== EventSource.CLOSED) return;
|
|
34
|
+
scheduleReopen(state);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function scheduleReopen(state: StreamState): void {
|
|
38
|
+
if (state.reopenTimer || state.isClosed) return;
|
|
39
|
+
state.reopenTimer = setTimeout(() => {
|
|
40
|
+
state.reopenTimer = null;
|
|
41
|
+
open(state);
|
|
42
|
+
}, REOPEN_DELAY_MS);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function open(state: StreamState): void {
|
|
46
|
+
if (state.isClosed) return;
|
|
47
|
+
state.source?.close();
|
|
48
|
+
const source = new EventSource(state.options.endpoint);
|
|
49
|
+
state.source = source;
|
|
50
|
+
source.addEventListener("open", () => {
|
|
51
|
+
state.isConnectionLost = false;
|
|
52
|
+
});
|
|
53
|
+
source.addEventListener("error", () => handleError(state));
|
|
54
|
+
source.addEventListener(RELOAD_EVENT_NAME, () => state.options.onReload());
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Keeps one `reload` stream open to the backend for as long as the page lives,
|
|
59
|
+
* across backend restarts, and warns once per lost connection.
|
|
60
|
+
*/
|
|
61
|
+
export function openDevReloadStream(
|
|
62
|
+
options: DevReloadStreamOptions,
|
|
63
|
+
): DevReloadStream {
|
|
64
|
+
const state: StreamState = {
|
|
65
|
+
options,
|
|
66
|
+
source: null,
|
|
67
|
+
reopenTimer: null,
|
|
68
|
+
isConnectionLost: false,
|
|
69
|
+
isClosed: false,
|
|
70
|
+
};
|
|
71
|
+
open(state);
|
|
72
|
+
return {
|
|
73
|
+
close: () => {
|
|
74
|
+
state.isClosed = true;
|
|
75
|
+
if (state.reopenTimer) clearTimeout(state.reopenTimer);
|
|
76
|
+
state.reopenTimer = null;
|
|
77
|
+
state.source?.close();
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { openDevReloadStream } from "../layers/dms-layout/app/utils/dev-reload-stream";
|
|
3
|
+
|
|
4
|
+
const ENDPOINT = "http://backend.test/dms/dev/reload";
|
|
5
|
+
// Comfortably past the delay before a source the browser gave up on is reopened.
|
|
6
|
+
const REOPEN_WAIT_MS = 5000;
|
|
7
|
+
|
|
8
|
+
type Listener = () => void;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Stands in for the browser's EventSource: the tests play the browser's part by
|
|
12
|
+
* firing its events, including the state it reaches before each one.
|
|
13
|
+
*/
|
|
14
|
+
class FakeEventSource {
|
|
15
|
+
static readonly CONNECTING = 0;
|
|
16
|
+
static readonly OPEN = 1;
|
|
17
|
+
static readonly CLOSED = 2;
|
|
18
|
+
static instances: FakeEventSource[] = [];
|
|
19
|
+
|
|
20
|
+
readyState = FakeEventSource.CONNECTING;
|
|
21
|
+
private readonly listeners = new Map<string, Listener[]>();
|
|
22
|
+
|
|
23
|
+
constructor(readonly url: string) {
|
|
24
|
+
FakeEventSource.instances.push(this);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
addEventListener(type: string, listener: Listener): void {
|
|
28
|
+
this.listeners.set(type, [...(this.listeners.get(type) ?? []), listener]);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
close(): void {
|
|
32
|
+
this.readyState = FakeEventSource.CLOSED;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
emit(type: string, readyState = this.readyState): void {
|
|
36
|
+
this.readyState = readyState;
|
|
37
|
+
for (const listener of this.listeners.get(type) ?? []) listener();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** The backend went away; the browser keeps retrying on its own. */
|
|
41
|
+
drop(): void {
|
|
42
|
+
this.emit("error", FakeEventSource.CONNECTING);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** The backend answered with something other than an event stream. */
|
|
46
|
+
refuse(): void {
|
|
47
|
+
this.emit("error", FakeEventSource.CLOSED);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function latestSource(): FakeEventSource {
|
|
52
|
+
const source = FakeEventSource.instances.at(-1);
|
|
53
|
+
if (!source) throw new Error("No EventSource was opened");
|
|
54
|
+
return source;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
describe("dev reload stream", () => {
|
|
58
|
+
let warn: ReturnType<typeof vi.spyOn>;
|
|
59
|
+
|
|
60
|
+
beforeEach(() => {
|
|
61
|
+
vi.useFakeTimers();
|
|
62
|
+
vi.stubGlobal("EventSource", FakeEventSource);
|
|
63
|
+
FakeEventSource.instances = [];
|
|
64
|
+
warn = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
afterEach(() => {
|
|
68
|
+
vi.useRealTimers();
|
|
69
|
+
vi.unstubAllGlobals();
|
|
70
|
+
vi.restoreAllMocks();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("forwards reload events", () => {
|
|
74
|
+
const onReload = vi.fn();
|
|
75
|
+
openDevReloadStream({ endpoint: ENDPOINT, onReload });
|
|
76
|
+
|
|
77
|
+
latestSource().emit("open", FakeEventSource.OPEN);
|
|
78
|
+
latestSource().emit("reload");
|
|
79
|
+
|
|
80
|
+
expect(latestSource().url).toBe(ENDPOINT);
|
|
81
|
+
expect(onReload).toHaveBeenCalledOnce();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("warns once while the browser retries a lost backend", () => {
|
|
85
|
+
openDevReloadStream({ endpoint: ENDPOINT, onReload: vi.fn() });
|
|
86
|
+
const source = latestSource();
|
|
87
|
+
source.emit("open", FakeEventSource.OPEN);
|
|
88
|
+
|
|
89
|
+
source.drop();
|
|
90
|
+
source.drop();
|
|
91
|
+
source.drop();
|
|
92
|
+
|
|
93
|
+
expect(warn).toHaveBeenCalledOnce();
|
|
94
|
+
// The browser owns these retries: no second source competes with it.
|
|
95
|
+
vi.advanceTimersByTime(REOPEN_WAIT_MS);
|
|
96
|
+
expect(FakeEventSource.instances).toHaveLength(1);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("warns again for the next loss once the stream is back", () => {
|
|
100
|
+
openDevReloadStream({ endpoint: ENDPOINT, onReload: vi.fn() });
|
|
101
|
+
const source = latestSource();
|
|
102
|
+
source.emit("open", FakeEventSource.OPEN);
|
|
103
|
+
|
|
104
|
+
source.drop();
|
|
105
|
+
source.emit("open", FakeEventSource.OPEN);
|
|
106
|
+
source.drop();
|
|
107
|
+
|
|
108
|
+
expect(warn).toHaveBeenCalledTimes(2);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("reopens a source the browser gave up on", () => {
|
|
112
|
+
const onReload = vi.fn();
|
|
113
|
+
openDevReloadStream({ endpoint: ENDPOINT, onReload });
|
|
114
|
+
latestSource().refuse();
|
|
115
|
+
|
|
116
|
+
vi.advanceTimersByTime(REOPEN_WAIT_MS);
|
|
117
|
+
const reopened = latestSource();
|
|
118
|
+
reopened.emit("open", FakeEventSource.OPEN);
|
|
119
|
+
reopened.emit("reload");
|
|
120
|
+
|
|
121
|
+
expect(FakeEventSource.instances).toHaveLength(2);
|
|
122
|
+
expect(onReload).toHaveBeenCalledOnce();
|
|
123
|
+
expect(warn).toHaveBeenCalledOnce();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("stays closed once closed", () => {
|
|
127
|
+
const stream = openDevReloadStream({
|
|
128
|
+
endpoint: ENDPOINT,
|
|
129
|
+
onReload: vi.fn(),
|
|
130
|
+
});
|
|
131
|
+
latestSource().refuse();
|
|
132
|
+
|
|
133
|
+
stream.close();
|
|
134
|
+
vi.advanceTimersByTime(REOPEN_WAIT_MS);
|
|
135
|
+
|
|
136
|
+
expect(FakeEventSource.instances).toHaveLength(1);
|
|
137
|
+
expect(latestSource().readyState).toBe(FakeEventSource.CLOSED);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { expect, it } from "vitest";
|
|
3
|
+
|
|
4
|
+
/** The room the page leaves between its blocks. */
|
|
5
|
+
const BLOCK_SPACING = "space-y-6";
|
|
6
|
+
|
|
7
|
+
const sourceOf = (file: string) =>
|
|
8
|
+
readFileSync(new URL(`../layers/${file}`, import.meta.url), "utf8");
|
|
9
|
+
|
|
10
|
+
// A tab holding two blocks stacked them edge to edge, while the page spaces
|
|
11
|
+
// the same two blocks placed outside a tab.
|
|
12
|
+
it("spaces the blocks of a tab the way the page spaces its own", () => {
|
|
13
|
+
expect(sourceOf("dms-layout/app/pages/[...slug].vue")).toContain(
|
|
14
|
+
`dms-page-stack ${BLOCK_SPACING}`,
|
|
15
|
+
);
|
|
16
|
+
expect(sourceOf("dms-ui/app/components/tab/Tab.vue")).toMatch(
|
|
17
|
+
new RegExp(
|
|
18
|
+
`<div class="[^"]*\\b${BLOCK_SPACING}\\b[^"]*">\\s*<slot :name="item.slot" />`,
|
|
19
|
+
),
|
|
20
|
+
);
|
|
21
|
+
});
|