@alloy-js/core 0.19.0-dev.0 → 0.19.0-dev.12
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/src/components/AppendFile.d.ts +90 -0
- package/dist/src/components/AppendFile.d.ts.map +1 -0
- package/dist/src/components/AppendFile.js +226 -0
- package/dist/src/components/CopyFile.d.ts +12 -0
- package/dist/src/components/CopyFile.d.ts.map +1 -0
- package/dist/src/components/CopyFile.js +15 -0
- package/dist/src/components/TemplateFile.d.ts +84 -0
- package/dist/src/components/TemplateFile.d.ts.map +1 -0
- package/dist/src/components/TemplateFile.js +133 -0
- package/dist/src/components/UpdateFile.d.ts +34 -0
- package/dist/src/components/UpdateFile.d.ts.map +1 -0
- package/dist/src/components/UpdateFile.js +66 -0
- package/dist/src/components/index.d.ts +4 -0
- package/dist/src/components/index.d.ts.map +1 -1
- package/dist/src/components/index.js +4 -0
- package/dist/src/components/stc/index.d.ts +4 -0
- package/dist/src/components/stc/index.d.ts.map +1 -1
- package/dist/src/components/stc/index.js +4 -0
- package/dist/src/context/source-directory.d.ts +3 -3
- package/dist/src/context/source-directory.d.ts.map +1 -1
- package/dist/src/context/source-file.d.ts +4 -0
- package/dist/src/context/source-file.d.ts.map +1 -1
- package/dist/src/debug.d.ts.map +1 -1
- package/dist/src/debug.js +4 -1
- package/dist/src/host/alloy-host.browser.d.ts +11 -0
- package/dist/src/host/alloy-host.browser.d.ts.map +1 -0
- package/dist/src/host/alloy-host.browser.js +31 -0
- package/dist/src/host/alloy-host.d.ts +11 -0
- package/dist/src/host/alloy-host.d.ts.map +1 -0
- package/dist/src/host/alloy-host.js +143 -0
- package/dist/src/host/interface.d.ts +144 -0
- package/dist/src/host/interface.d.ts.map +1 -0
- package/dist/src/host/interface.js +1 -0
- package/dist/src/index.browser.d.ts +1 -1
- package/dist/src/index.browser.d.ts.map +1 -1
- package/dist/src/index.browser.js +2 -2
- package/dist/src/reactivity.js +2 -2
- package/dist/src/render.d.ts +10 -2
- package/dist/src/render.d.ts.map +1 -1
- package/dist/src/render.js +20 -1
- package/dist/src/resource.d.ts +80 -0
- package/dist/src/resource.d.ts.map +1 -0
- package/dist/src/resource.js +118 -0
- package/dist/src/scheduler.d.ts +6 -0
- package/dist/src/scheduler.d.ts.map +1 -1
- package/dist/src/scheduler.js +36 -0
- package/dist/src/write-output.d.ts +1 -1
- package/dist/src/write-output.d.ts.map +1 -1
- package/dist/src/write-output.js +40 -21
- package/dist/test/components/append-file.test.d.ts +2 -0
- package/dist/test/components/append-file.test.d.ts.map +1 -0
- package/dist/test/components/append-file.test.js +281 -0
- package/dist/test/components/copy-file.test.d.ts +2 -0
- package/dist/test/components/copy-file.test.d.ts.map +1 -0
- package/dist/test/components/copy-file.test.js +94 -0
- package/dist/test/components/source-file.test.d.ts.map +1 -1
- package/dist/test/components/template-file.test.d.ts +2 -0
- package/dist/test/components/template-file.test.d.ts.map +1 -0
- package/dist/test/components/template-file.test.js +133 -0
- package/dist/test/components/update-file.test.d.ts +2 -0
- package/dist/test/components/update-file.test.d.ts.map +1 -0
- package/dist/test/components/update-file.test.js +169 -0
- package/dist/test/rendering/formatting.test.d.ts.map +1 -1
- package/dist/testing/extend-expect.d.ts +0 -15
- package/dist/testing/extend-expect.d.ts.map +1 -1
- package/dist/testing/extend-expect.js +89 -12
- package/dist/testing/extend-expect.test.d.ts +2 -0
- package/dist/testing/extend-expect.test.d.ts.map +1 -0
- package/dist/testing/extend-expect.test.js +149 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/components/AppendFile.tsx +294 -0
- package/src/components/CopyFile.tsx +29 -0
- package/src/components/TemplateFile.tsx +193 -0
- package/src/components/UpdateFile.tsx +86 -0
- package/src/components/index.tsx +4 -0
- package/src/components/stc/index.ts +4 -0
- package/src/context/source-directory.ts +5 -3
- package/src/context/source-file.ts +5 -0
- package/src/debug.ts +4 -1
- package/src/host/alloy-host.browser.ts +56 -0
- package/src/host/alloy-host.ts +160 -0
- package/src/host/interface.ts +153 -0
- package/src/index.browser.ts +1 -1
- package/src/reactivity.ts +2 -2
- package/src/render.ts +44 -5
- package/src/resource.ts +152 -0
- package/src/scheduler.ts +39 -0
- package/src/write-output.ts +49 -19
- package/temp/api.json +2009 -546
- package/test/components/append-file.test.tsx +275 -0
- package/test/components/copy-file.test.tsx +98 -0
- package/test/components/source-file.test.tsx +5 -2
- package/test/components/template-file.test.tsx +127 -0
- package/test/components/update-file.test.tsx +214 -0
- package/test/rendering/formatting.test.tsx +9 -3
- package/testing/extend-expect.test.tsx +126 -0
- package/testing/extend-expect.ts +123 -23
- package/testing/vitest.d.ts +9 -1
- package/tsconfig.json +2 -1
- package/dist/src/write-output.browser.d.ts +0 -2
- package/dist/src/write-output.browser.d.ts.map +0 -1
- package/dist/src/write-output.browser.js +0 -4
- package/src/write-output.browser.ts +0 -4
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { existsSync, unlinkSync, writeFileSync } from "fs";
|
|
2
|
+
import { tmpdir } from "os";
|
|
3
|
+
import { join } from "pathe";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
5
|
+
import { UpdateFile } from "../../src/components/UpdateFile.jsx";
|
|
6
|
+
import { render } from "../../src/render.js";
|
|
7
|
+
import "../../testing/extend-expect.js";
|
|
8
|
+
import { d } from "../../testing/render.js";
|
|
9
|
+
|
|
10
|
+
describe("UpdateFile", () => {
|
|
11
|
+
let testFilePath: string;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
testFilePath = join(tmpdir(), "test-file.txt");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
if (existsSync(testFilePath)) {
|
|
19
|
+
unlinkSync(testFilePath);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe("when file exists", () => {
|
|
24
|
+
it("should read existing file and transform content", async () => {
|
|
25
|
+
const existingContent = "Hello World";
|
|
26
|
+
writeFileSync(testFilePath, existingContent, "utf-8");
|
|
27
|
+
|
|
28
|
+
const result = (
|
|
29
|
+
<UpdateFile path={testFilePath}>
|
|
30
|
+
{(currentContents) => {
|
|
31
|
+
return `${currentContents?.toUpperCase()}`;
|
|
32
|
+
}}
|
|
33
|
+
</UpdateFile>
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
await expect(result).toRenderToAsync("HELLO WORLD");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("should handle JSON file updates", async () => {
|
|
40
|
+
const existingConfig = { version: "1.0.0", name: "test" };
|
|
41
|
+
writeFileSync(testFilePath, JSON.stringify(existingConfig), "utf-8");
|
|
42
|
+
|
|
43
|
+
const result = (
|
|
44
|
+
<UpdateFile path={testFilePath}>
|
|
45
|
+
{(currentContents) => {
|
|
46
|
+
const config = JSON.parse(currentContents!);
|
|
47
|
+
config.version = "2.0.0";
|
|
48
|
+
config.newProperty = "added";
|
|
49
|
+
return JSON.stringify(config, null, 2);
|
|
50
|
+
}}
|
|
51
|
+
</UpdateFile>
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
await expect(result).toRenderToAsync(d`
|
|
55
|
+
{
|
|
56
|
+
"version": "2.0.0",
|
|
57
|
+
"name": "test",
|
|
58
|
+
"newProperty": "added"
|
|
59
|
+
}
|
|
60
|
+
`);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("should handle multiline content transformation", async () => {
|
|
64
|
+
const existingContent = d`
|
|
65
|
+
line 1
|
|
66
|
+
line 2
|
|
67
|
+
line 3
|
|
68
|
+
`;
|
|
69
|
+
writeFileSync(testFilePath, existingContent, "utf-8");
|
|
70
|
+
|
|
71
|
+
const result = (
|
|
72
|
+
<UpdateFile path={testFilePath}>
|
|
73
|
+
{(currentContents) => {
|
|
74
|
+
const lines = currentContents!.split("\n");
|
|
75
|
+
const modifiedLines = lines.map((line) => `// ${line}`);
|
|
76
|
+
return modifiedLines.join("\n");
|
|
77
|
+
}}
|
|
78
|
+
</UpdateFile>
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
await expect(result).toRenderToAsync(d`
|
|
82
|
+
// line 1
|
|
83
|
+
// line 2
|
|
84
|
+
// line 3
|
|
85
|
+
`);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("should handle empty existing file", async () => {
|
|
89
|
+
writeFileSync(testFilePath, "", "utf-8");
|
|
90
|
+
|
|
91
|
+
const result = (
|
|
92
|
+
<UpdateFile path={testFilePath}>
|
|
93
|
+
{(currentContents) => {
|
|
94
|
+
return `Content was: "${currentContents || "empty"}"`;
|
|
95
|
+
}}
|
|
96
|
+
</UpdateFile>
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
await expect(result).toRenderToAsync('Content was: "empty"');
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe("when file does not exist", () => {
|
|
104
|
+
it("should use defaultContent when file does not exist", async () => {
|
|
105
|
+
const result = (
|
|
106
|
+
<UpdateFile path="non-existent.txt" defaultContent="default content">
|
|
107
|
+
{(currentContents) => {
|
|
108
|
+
if (currentContents === null) {
|
|
109
|
+
return `File did not exist`;
|
|
110
|
+
}
|
|
111
|
+
return `File existed with: ${currentContents}`;
|
|
112
|
+
}}
|
|
113
|
+
</UpdateFile>
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
await expect(result).toRenderToAsync("File did not exist");
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("should use defaultContentPath when file does not exist", async () => {
|
|
120
|
+
const defaultFilePath = join(
|
|
121
|
+
tmpdir(),
|
|
122
|
+
`default-content-${Date.now()}.txt`,
|
|
123
|
+
);
|
|
124
|
+
const defaultContent = "This is default content from file";
|
|
125
|
+
writeFileSync(defaultFilePath, defaultContent, "utf-8");
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
const result = (
|
|
129
|
+
<UpdateFile
|
|
130
|
+
path="non-existent.txt"
|
|
131
|
+
defaultContentPath={defaultFilePath}
|
|
132
|
+
>
|
|
133
|
+
{(currentContents) => {
|
|
134
|
+
return `Default: ${currentContents}`;
|
|
135
|
+
}}
|
|
136
|
+
</UpdateFile>
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
await expect(result).toRenderToAsync(
|
|
140
|
+
"Default: This is default content from file",
|
|
141
|
+
);
|
|
142
|
+
} finally {
|
|
143
|
+
if (existsSync(defaultFilePath)) {
|
|
144
|
+
unlinkSync(defaultFilePath);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("should pass null to children when no default content provided", async () => {
|
|
150
|
+
const result = (
|
|
151
|
+
<UpdateFile path="non-existent.txt">
|
|
152
|
+
{(currentContents) => {
|
|
153
|
+
if (currentContents === null) {
|
|
154
|
+
return `Creating new file`;
|
|
155
|
+
}
|
|
156
|
+
return `Updating existing file: ${currentContents}`;
|
|
157
|
+
}}
|
|
158
|
+
</UpdateFile>
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
await expect(result).toRenderToAsync("Creating new file");
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it("should create JSON file from scratch when it doesn't exist", async () => {
|
|
165
|
+
const result = (
|
|
166
|
+
<UpdateFile path="new-config.json">
|
|
167
|
+
{(currentContents) => {
|
|
168
|
+
const config = currentContents ? JSON.parse(currentContents) : {};
|
|
169
|
+
config.name = "new-project";
|
|
170
|
+
config.version = "1.0.0";
|
|
171
|
+
config.dependencies = {};
|
|
172
|
+
return JSON.stringify(config, null, 2);
|
|
173
|
+
}}
|
|
174
|
+
</UpdateFile>
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
await expect(result).toRenderToAsync(d`
|
|
178
|
+
{
|
|
179
|
+
"name": "new-project",
|
|
180
|
+
"version": "1.0.0",
|
|
181
|
+
"dependencies": {}
|
|
182
|
+
}
|
|
183
|
+
`);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
describe("error handling", () => {
|
|
188
|
+
it("should throw error when defaultContentPath file does not exist", async () => {
|
|
189
|
+
expect(() =>
|
|
190
|
+
render(
|
|
191
|
+
<UpdateFile
|
|
192
|
+
path="non-existent.txt"
|
|
193
|
+
defaultContentPath="/path/that/does/not/exist.txt"
|
|
194
|
+
>
|
|
195
|
+
{(currentContents) => currentContents}
|
|
196
|
+
</UpdateFile>,
|
|
197
|
+
),
|
|
198
|
+
).toThrow();
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it("should handle file system errors gracefully", async () => {
|
|
202
|
+
// Try to read a directory as a file (should cause an error)
|
|
203
|
+
const dirPath = join(tmpdir(), `test-dir-${Date.now()}`);
|
|
204
|
+
|
|
205
|
+
expect(() =>
|
|
206
|
+
render(
|
|
207
|
+
<UpdateFile path={dirPath}>
|
|
208
|
+
{(currentContents) => currentContents}
|
|
209
|
+
</UpdateFile>,
|
|
210
|
+
),
|
|
211
|
+
).toThrow();
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
});
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
ContentOutputFile,
|
|
4
|
+
For,
|
|
5
|
+
Output,
|
|
6
|
+
render,
|
|
7
|
+
SourceFile,
|
|
8
|
+
} from "../../src/index.js";
|
|
3
9
|
import "../../testing/extend-expect.js";
|
|
4
10
|
import { d } from "../../testing/render.js";
|
|
5
11
|
|
|
@@ -454,7 +460,7 @@ it("formats based on the output component props", () => {
|
|
|
454
460
|
);
|
|
455
461
|
|
|
456
462
|
const tree = render(template);
|
|
457
|
-
expect(tree.contents[0].contents).toEqual(d`
|
|
463
|
+
expect((tree.contents[0] as ContentOutputFile).contents).toEqual(d`
|
|
458
464
|
1
|
|
459
465
|
2
|
|
460
466
|
3
|
|
@@ -476,7 +482,7 @@ it("formats based on the source file component props", () => {
|
|
|
476
482
|
);
|
|
477
483
|
|
|
478
484
|
const tree = render(template);
|
|
479
|
-
expect(tree.contents[0].contents).toEqual(d`
|
|
485
|
+
expect((tree.contents[0] as ContentOutputFile).contents).toEqual(d`
|
|
480
486
|
1
|
|
481
487
|
2
|
|
482
488
|
3
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Indent, Output, SourceDirectory, SourceFile } from "@alloy-js/core";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import "./extend-expect.js";
|
|
4
|
+
|
|
5
|
+
describe("toRenderTo", () => {
|
|
6
|
+
it("can validate structure without source files", () => {
|
|
7
|
+
expect(
|
|
8
|
+
<>
|
|
9
|
+
base
|
|
10
|
+
<Indent>indented</Indent>
|
|
11
|
+
</>,
|
|
12
|
+
).toRenderTo(`
|
|
13
|
+
base
|
|
14
|
+
indented
|
|
15
|
+
`);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("can expect the content of the single source file used", () => {
|
|
19
|
+
expect(
|
|
20
|
+
<Output>
|
|
21
|
+
<SourceFile path="Test.cs" tabWidth={4} filetype="csharp">
|
|
22
|
+
base
|
|
23
|
+
<Indent>indented</Indent>
|
|
24
|
+
</SourceFile>
|
|
25
|
+
</Output>,
|
|
26
|
+
).toRenderTo(`
|
|
27
|
+
base
|
|
28
|
+
indented
|
|
29
|
+
`);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("expect with specific file name", () => {
|
|
33
|
+
expect(
|
|
34
|
+
<Output>
|
|
35
|
+
<SourceFile path="Test.cs" tabWidth={4} filetype="csharp">
|
|
36
|
+
base
|
|
37
|
+
<Indent>indented</Indent>
|
|
38
|
+
</SourceFile>
|
|
39
|
+
</Output>,
|
|
40
|
+
).toRenderTo({
|
|
41
|
+
"Test.cs": `
|
|
42
|
+
base
|
|
43
|
+
indented
|
|
44
|
+
`,
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("expect with specific file name in SourceDirectory", () => {
|
|
49
|
+
expect(
|
|
50
|
+
<Output>
|
|
51
|
+
<SourceDirectory path="src">
|
|
52
|
+
<SourceFile path="test.txt" filetype="text">
|
|
53
|
+
content
|
|
54
|
+
</SourceFile>
|
|
55
|
+
</SourceDirectory>
|
|
56
|
+
</Output>,
|
|
57
|
+
).toRenderTo({
|
|
58
|
+
"src/test.txt": `
|
|
59
|
+
content
|
|
60
|
+
`,
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("each source file respect their print options", () => {
|
|
65
|
+
expect(
|
|
66
|
+
<Output>
|
|
67
|
+
<SourceFile path="Test.cs" tabWidth={4} filetype="csharp">
|
|
68
|
+
base1
|
|
69
|
+
<Indent>indented</Indent>
|
|
70
|
+
</SourceFile>
|
|
71
|
+
<SourceFile path="Test.ts" tabWidth={2} filetype="csharp">
|
|
72
|
+
base2
|
|
73
|
+
<Indent>indented</Indent>
|
|
74
|
+
</SourceFile>
|
|
75
|
+
</Output>,
|
|
76
|
+
).toRenderTo({
|
|
77
|
+
"Test.cs": `
|
|
78
|
+
base1
|
|
79
|
+
indented
|
|
80
|
+
`,
|
|
81
|
+
"Test.ts": `
|
|
82
|
+
base2
|
|
83
|
+
indented
|
|
84
|
+
`,
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("fails when content does not match", () => {
|
|
89
|
+
expect(() =>
|
|
90
|
+
expect(
|
|
91
|
+
<>
|
|
92
|
+
base
|
|
93
|
+
<Indent>indented</Indent>
|
|
94
|
+
</>,
|
|
95
|
+
).toRenderTo(`
|
|
96
|
+
base
|
|
97
|
+
wrong
|
|
98
|
+
`),
|
|
99
|
+
).toThrowError(/Render is incorrect/);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("fails when files don't match", () => {
|
|
103
|
+
expect(() =>
|
|
104
|
+
expect(
|
|
105
|
+
<Output>
|
|
106
|
+
<SourceFile path="Test.cs" tabWidth={4} filetype="csharp">
|
|
107
|
+
base1
|
|
108
|
+
<Indent>indented</Indent>
|
|
109
|
+
</SourceFile>
|
|
110
|
+
<SourceFile path="Test.ts" tabWidth={2} filetype="typescript">
|
|
111
|
+
base2
|
|
112
|
+
<Indent>indented</Indent>
|
|
113
|
+
</SourceFile>
|
|
114
|
+
</Output>,
|
|
115
|
+
).toRenderTo({
|
|
116
|
+
"Test.cs": `
|
|
117
|
+
bad
|
|
118
|
+
`,
|
|
119
|
+
"Test.ts": `
|
|
120
|
+
base2
|
|
121
|
+
indented
|
|
122
|
+
`,
|
|
123
|
+
}),
|
|
124
|
+
).toThrowError(/Render is incorrect/);
|
|
125
|
+
});
|
|
126
|
+
});
|
package/testing/extend-expect.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Children,
|
|
3
|
+
getContextForRenderNode,
|
|
4
|
+
printTree,
|
|
5
|
+
RenderedTextTree,
|
|
6
|
+
renderTree,
|
|
7
|
+
} from "@alloy-js/core";
|
|
8
|
+
import "vitest";
|
|
2
9
|
import { expect } from "vitest";
|
|
10
|
+
import { flushJobs, flushJobsAsync } from "../src/scheduler.js";
|
|
3
11
|
import { dedent } from "./render.js";
|
|
4
12
|
|
|
5
13
|
interface ToRenderToOptions {
|
|
@@ -11,36 +19,128 @@ interface ToRenderToOptions {
|
|
|
11
19
|
expect.extend({
|
|
12
20
|
toRenderTo(
|
|
13
21
|
received: Children,
|
|
14
|
-
expectedRaw: string,
|
|
22
|
+
expectedRaw: string | Record<string, string>,
|
|
23
|
+
renderOptions?: ToRenderToOptions,
|
|
24
|
+
) {
|
|
25
|
+
const tree = renderTree(received);
|
|
26
|
+
flushJobs();
|
|
27
|
+
const actual = getFilesFromTree(tree, renderOptions);
|
|
28
|
+
|
|
29
|
+
return validateRender(actual, expectedRaw, this.isNot);
|
|
30
|
+
},
|
|
31
|
+
async toRenderToAsync(
|
|
32
|
+
received: Children,
|
|
33
|
+
expectedRaw: string | Record<string, string>,
|
|
15
34
|
renderOptions?: ToRenderToOptions,
|
|
16
35
|
) {
|
|
17
|
-
const { isNot } = this;
|
|
18
36
|
const tree = renderTree(received);
|
|
19
|
-
|
|
37
|
+
await flushJobsAsync();
|
|
38
|
+
const actual = getFilesFromTree(tree, renderOptions);
|
|
39
|
+
return validateRender(actual, expectedRaw, this.isNot);
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
function validateRender(
|
|
44
|
+
actual: string | Record<string, string>,
|
|
45
|
+
expectedRaw: string | Record<string, string>,
|
|
46
|
+
isNot: boolean,
|
|
47
|
+
) {
|
|
48
|
+
const message = () => `Render is${isNot ? " not" : ""} incorrect`;
|
|
49
|
+
|
|
50
|
+
if (typeof expectedRaw === "string") {
|
|
20
51
|
const expected = dedent(expectedRaw);
|
|
52
|
+
let actualStr;
|
|
53
|
+
if (typeof actual === "string") {
|
|
54
|
+
actualStr = actual;
|
|
55
|
+
} else if (Object.keys(actual).length === 1) {
|
|
56
|
+
// If we have a single file, we can use its content directly.
|
|
57
|
+
actualStr = Object.values(actual)[0];
|
|
58
|
+
} else {
|
|
59
|
+
return {
|
|
60
|
+
pass: false,
|
|
61
|
+
message,
|
|
62
|
+
actual,
|
|
63
|
+
expected,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
21
66
|
return {
|
|
22
|
-
pass:
|
|
23
|
-
message
|
|
24
|
-
|
|
25
|
-
},
|
|
26
|
-
actual,
|
|
67
|
+
pass: actualStr === expected,
|
|
68
|
+
message,
|
|
69
|
+
actual: actualStr,
|
|
27
70
|
expected,
|
|
28
71
|
};
|
|
29
|
-
}
|
|
30
|
-
|
|
72
|
+
} else if (typeof actual === "object" && typeof expectedRaw === "object") {
|
|
73
|
+
const expected = expectedRaw;
|
|
74
|
+
const dedentExpected: Record<string, string> = {};
|
|
75
|
+
for (const [key, value] of Object.entries(expected)) {
|
|
76
|
+
dedentExpected[key] = dedent(value);
|
|
77
|
+
}
|
|
78
|
+
const pass =
|
|
79
|
+
Object.keys(actual).length === Object.keys(expected).length &&
|
|
80
|
+
Object.entries(actual).every(([key, value]) => {
|
|
81
|
+
return dedentExpected[key] === value;
|
|
82
|
+
});
|
|
83
|
+
return {
|
|
84
|
+
pass,
|
|
85
|
+
message,
|
|
86
|
+
actual,
|
|
87
|
+
expected: dedentExpected,
|
|
88
|
+
};
|
|
89
|
+
} else {
|
|
90
|
+
return {
|
|
91
|
+
pass: false,
|
|
92
|
+
message,
|
|
93
|
+
actual,
|
|
94
|
+
expected: expectedRaw,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
31
98
|
|
|
32
|
-
|
|
99
|
+
function getFilesFromTree(tree: RenderedTextTree, options?: ToRenderToOptions) {
|
|
100
|
+
const files: Record<string, string> = {};
|
|
101
|
+
// when passing Output, the first render tree child is the Output component.
|
|
102
|
+
const rootRenderOptions =
|
|
103
|
+
Array.isArray(tree) ?
|
|
104
|
+
(getContextForRenderNode(tree[0] as RenderedTextTree)?.meta
|
|
105
|
+
?.printOptions ?? {})
|
|
106
|
+
: {};
|
|
33
107
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
108
|
+
collectSourceFiles(tree);
|
|
109
|
+
// If we found no source files, we return the tree as a string.
|
|
110
|
+
if (Object.keys(files).length === 0) {
|
|
111
|
+
return printTree(tree, options);
|
|
112
|
+
} else {
|
|
113
|
+
return files;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function collectSourceFiles(root: RenderedTextTree) {
|
|
117
|
+
if (!Array.isArray(root)) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const context = getContextForRenderNode(root);
|
|
121
|
+
if (context?.meta?.sourceFile) {
|
|
122
|
+
files[context.meta.sourceFile.path] = printTree(root, {
|
|
123
|
+
printWidth:
|
|
124
|
+
options?.printWidth ??
|
|
125
|
+
context.meta?.printOptions?.printWidth ??
|
|
126
|
+
rootRenderOptions.printWidth,
|
|
127
|
+
tabWidth:
|
|
128
|
+
options?.tabWidth ??
|
|
129
|
+
context.meta?.printOptions?.tabWidth ??
|
|
130
|
+
rootRenderOptions.tabWidth,
|
|
131
|
+
useTabs:
|
|
132
|
+
options?.useTabs ??
|
|
133
|
+
context.meta?.printOptions?.useTabs ??
|
|
134
|
+
rootRenderOptions.useTabs,
|
|
135
|
+
});
|
|
136
|
+
} else {
|
|
137
|
+
visitChildren();
|
|
138
|
+
}
|
|
42
139
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
140
|
+
function visitChildren() {
|
|
141
|
+
for (const child of root) {
|
|
142
|
+
collectSourceFiles(child as RenderedTextTree);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
46
146
|
}
|
package/testing/vitest.d.ts
CHANGED
|
@@ -5,8 +5,16 @@ interface ToRenderToRenderOptions {
|
|
|
5
5
|
tabWidth?: number;
|
|
6
6
|
useTabs?: boolean;
|
|
7
7
|
}
|
|
8
|
+
|
|
8
9
|
interface CustomMatchers<R = unknown> {
|
|
9
|
-
toRenderTo: (
|
|
10
|
+
toRenderTo: (
|
|
11
|
+
str: string | Record<string, string>,
|
|
12
|
+
options?: ToRenderToRenderOptions,
|
|
13
|
+
) => R;
|
|
14
|
+
toRenderToAsync: (
|
|
15
|
+
str: string | Record<string, string>,
|
|
16
|
+
options?: ToRenderToRenderOptions,
|
|
17
|
+
) => Promise<R>;
|
|
10
18
|
}
|
|
11
19
|
|
|
12
20
|
declare module "vitest" {
|
package/tsconfig.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"write-output.browser.d.ts","sourceRoot":"","sources":["../../src/write-output.browser.ts"],"names":[],"mappings":"AAAA,wBAAgB,WAAW,CAAC,MAAM,EAAE,GAAG,EAAE,QAAQ,GAAE,MAAW,QAG7D"}
|