@alloy-js/core 0.18.0-dev.5 → 0.18.1

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.
@@ -51,7 +51,7 @@ it("works with string keys", () => {
51
51
  );
52
52
  });
53
53
 
54
- const tree = render(
54
+ const _tree = render(
55
55
  <Output>
56
56
  <SourceFile path="test.ts" filetype="ts">
57
57
  <MyFunctionComponent name="foo" />
@@ -59,7 +59,7 @@ it("works with string keys", () => {
59
59
  </Output>,
60
60
  );
61
61
 
62
- console.log(tree.contents[0].contents);
62
+ // console.log(tree.contents[0].contents);
63
63
  });
64
64
 
65
65
  it("works with symbols", () => {
@@ -79,7 +79,6 @@ it("works with symbols", () => {
79
79
  }
80
80
 
81
81
  function MyFunctionComponent(props: FunctionComponentProps) {
82
- const binder = useBinder();
83
82
  const sym = new OutputSymbol(props.name, {
84
83
  refkeys: refkey(),
85
84
  });
@@ -108,7 +107,7 @@ it("works with symbols", () => {
108
107
  );
109
108
  });
110
109
 
111
- const tree = render(
110
+ const _tree = render(
112
111
  <Output>
113
112
  <SourceFile path="test.ts" filetype="ts">
114
113
  <Scope name="foo">
@@ -118,7 +117,7 @@ it("works with symbols", () => {
118
117
  </Output>,
119
118
  );
120
119
 
121
- console.log(tree.contents[0].contents);
120
+ // console.log(tree.contents[0].contents);
122
121
  });
123
122
 
124
123
  it("can rename", () => {
@@ -161,7 +160,7 @@ it("can rename", () => {
161
160
  return binder!.resolveFQN("foo.bar") as Ref<OutputSymbol | undefined>;
162
161
  }, "bazxxx");
163
162
 
164
- const tree = render(
163
+ const _tree = render(
165
164
  <Output>
166
165
  <SourceFile path="test.ts" filetype="ts">
167
166
  <Scope name="foo">
@@ -171,5 +170,5 @@ it("can rename", () => {
171
170
  </Output>,
172
171
  );
173
172
 
174
- console.log(tree.contents[0].contents);
173
+ // console.log(tree.contents[0].contents);
175
174
  });
@@ -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
+ });
@@ -1,6 +1,14 @@
1
- import { Children, printTree, renderTree } from "@alloy-js/core";
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";
3
10
  import { dedent } from "./render.js";
11
+ import "./vitest.d.ts";
4
12
 
5
13
  interface ToRenderToOptions {
6
14
  printWidth?: number;
@@ -11,36 +19,113 @@ interface ToRenderToOptions {
11
19
  expect.extend({
12
20
  toRenderTo(
13
21
  received: Children,
14
- expectedRaw: string,
22
+ expectedRaw: string | Record<string, string>,
15
23
  renderOptions?: ToRenderToOptions,
16
24
  ) {
25
+ const message = () => `Render is${isNot ? " not" : ""} incorrect`;
26
+
17
27
  const { isNot } = this;
18
- const tree = renderTree(received);
19
- const actual = printTree(tree, renderOptions);
20
- const expected = dedent(expectedRaw);
21
- return {
22
- pass: actual === expected,
23
- message: () => {
24
- return `Render is${isNot ? " not" : ""} incorrect`;
25
- },
26
- actual,
27
- expected,
28
- };
28
+ const actual = renderAsFiles(received, renderOptions);
29
+ if (typeof expectedRaw === "string") {
30
+ const expected = dedent(expectedRaw);
31
+ let actualStr;
32
+ if (typeof actual === "string") {
33
+ actualStr = actual;
34
+ } else if (Object.keys(actual).length === 1) {
35
+ // If we have a single file, we can use its content directly.
36
+ actualStr = Object.values(actual)[0];
37
+ } else {
38
+ return {
39
+ pass: false,
40
+ message,
41
+ actual,
42
+ expected,
43
+ };
44
+ }
45
+ return {
46
+ pass: actualStr === expected,
47
+ message,
48
+ actual: actualStr,
49
+ expected,
50
+ };
51
+ } else if (typeof actual === "object" && typeof expectedRaw === "object") {
52
+ const expected = expectedRaw;
53
+ const dedentExpected: Record<string, string> = {};
54
+ for (const [key, value] of Object.entries(expected)) {
55
+ dedentExpected[key] = dedent(value);
56
+ }
57
+ const pass =
58
+ Object.keys(actual).length === Object.keys(expected).length &&
59
+ Object.entries(actual).every(([key, value]) => {
60
+ return dedentExpected[key] === value;
61
+ });
62
+ return {
63
+ pass,
64
+ message,
65
+ actual,
66
+ expected: dedentExpected,
67
+ };
68
+ } else {
69
+ return {
70
+ pass: false,
71
+ message,
72
+ actual,
73
+ expected: expectedRaw,
74
+ };
75
+ }
29
76
  },
30
77
  });
31
78
 
32
- import "vitest";
79
+ function renderAsFiles(
80
+ received: Children,
81
+ options?: ToRenderToOptions,
82
+ ): Record<string, string> | string {
83
+ const files: Record<string, string> = {};
84
+ const tree = renderTree(received);
33
85
 
34
- interface ToRenderToRenderOptions {
35
- printWidth?: number;
36
- tabWidth?: number;
37
- useTabs?: boolean;
38
- }
39
- interface CustomMatchers<R = unknown> {
40
- toRenderTo: (str: string, options?: ToRenderToRenderOptions) => R;
41
- }
86
+ // when passing Output, the first render tree child is the Output component.
87
+ const rootRenderOptions =
88
+ Array.isArray(tree) ?
89
+ (getContextForRenderNode(tree[0] as RenderedTextTree)?.meta
90
+ ?.printOptions ?? {})
91
+ : {};
92
+
93
+ collectSourceFiles(tree);
94
+ function collectSourceFiles(root: RenderedTextTree) {
95
+ if (!Array.isArray(root)) {
96
+ return;
97
+ }
98
+ const context = getContextForRenderNode(root);
99
+ if (context?.meta?.sourceFile) {
100
+ files[context.meta.sourceFile.path] = printTree(root, {
101
+ printWidth:
102
+ options?.printWidth ??
103
+ context.meta?.printOptions?.printWidth ??
104
+ rootRenderOptions.printWidth,
105
+ tabWidth:
106
+ options?.tabWidth ??
107
+ context.meta?.printOptions?.tabWidth ??
108
+ rootRenderOptions.tabWidth,
109
+ useTabs:
110
+ options?.useTabs ??
111
+ context.meta?.printOptions?.useTabs ??
112
+ rootRenderOptions.useTabs,
113
+ });
114
+ } else {
115
+ visitChildren();
116
+ }
117
+
118
+ function visitChildren() {
119
+ for (const child of root) {
120
+ collectSourceFiles(child as RenderedTextTree);
121
+ }
122
+ }
123
+ }
42
124
 
43
- declare module "vitest" {
44
- interface Assertion<T = any> extends CustomMatchers<T> {}
45
- interface AsymmetricMatchersContaining extends CustomMatchers {}
125
+ // If we found no source files, we return the tree as a string.
126
+ if (Object.keys(files).length === 0) {
127
+ return printTree(tree, options);
128
+ } else {
129
+ return files;
130
+ }
46
131
  }
@@ -5,8 +5,12 @@ interface ToRenderToRenderOptions {
5
5
  tabWidth?: number;
6
6
  useTabs?: boolean;
7
7
  }
8
+
8
9
  interface CustomMatchers<R = unknown> {
9
- toRenderTo: (str: string, options?: ToRenderToRenderOptions) => R;
10
+ toRenderTo: (
11
+ str: string | Record<string, string>,
12
+ options?: ToRenderToRenderOptions,
13
+ ) => R;
10
14
  }
11
15
 
12
16
  declare module "vitest" {
package/tsconfig.json CHANGED
@@ -11,7 +11,8 @@
11
11
  "test/**/*.ts",
12
12
  "test/**/*.tsx",
13
13
  "testing/**/*.ts",
14
- "testing/**/*.d.ts"
14
+ "testing/**/*.d.ts",
15
+ "testing/extend-expect.test.tsx"
15
16
  ],
16
17
  "exclude": ["node_modules", "dist"]
17
18
  }