Skip to content

Commit b10e940

Browse files
authored
Shared configs/workflow/cjsresolver (#144)
* use remote packages for incrementing. * add jsdom * wait for dom to load * add tests * lint * define package version * update tests * [email protected] * [email protected]
1 parent 8914710 commit b10e940

File tree

7 files changed

+504
-25
lines changed

7 files changed

+504
-25
lines changed

.github/workflows/deploy-apps.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ jobs:
5454
uses: pnpm/[email protected]
5555
- name: Install dependencies
5656
if: steps.changes.outputs.skip_steps != 'true'
57-
run: pnpm i
57+
run: pnpm install --prefer-workspace-packages=false
5858
- name: Build
5959
if: steps.changes.outputs.skip_steps != 'true'
6060
run: pnpm build apps

apps/nutritionfacts/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,5 @@
2828
"update-cache": "vite-node ./scripts/updateCache.mts"
2929
},
3030
"type": "module",
31-
"version": "1.5.1"
31+
"version": "1.5.2"
3232
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/**
2+
* @vitest-environment jsdom
3+
*/
4+
5+
import * as React from "react";
6+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
7+
8+
// --- Mock react-dom/client for ESM ---
9+
const renderSpy = vi.fn();
10+
const createRootMock = vi.fn(() => ({ render: renderSpy }));
11+
vi.mock("react-dom/client", () => ({ createRoot: createRootMock }));
12+
13+
// Mock InstUISettingsProvider and App
14+
vi.mock("@instructure/ui", () => ({
15+
InstUISettingsProvider: ({ children }: { children: React.ReactNode }) => (
16+
<div data-testid="instui-provider">{children}</div>
17+
),
18+
}));
19+
vi.mock("./App", () => ({
20+
default: function App() {
21+
return <div data-testid="app" />;
22+
},
23+
}));
24+
25+
declare global {
26+
// eslint-disable-next-line no-var
27+
var __vite_ssr_module_cache__: Record<string, unknown> | undefined;
28+
}
29+
30+
describe("main.tsx", () => {
31+
let rootElem: HTMLElement | null;
32+
33+
beforeEach(() => {
34+
// Remove cached module to force re-execution for every test
35+
const viteCache = globalThis.__vite_ssr_module_cache__;
36+
if (typeof viteCache === "object" && viteCache !== null) {
37+
Object.keys(viteCache).forEach((key) => {
38+
if (key.includes("/main.tsx")) {
39+
delete viteCache[key];
40+
}
41+
});
42+
}
43+
rootElem = document.createElement("div");
44+
rootElem.id = "root";
45+
document.body.appendChild(rootElem);
46+
renderSpy.mockClear();
47+
createRootMock.mockClear();
48+
});
49+
50+
afterEach(() => {
51+
const elem = document.getElementById("root");
52+
if (elem) elem.remove();
53+
});
54+
55+
it("throws an error if root element is not found", async () => {
56+
if (rootElem?.parentNode) {
57+
rootElem.parentNode.removeChild(rootElem);
58+
}
59+
// Remove cached module to force re-execution
60+
const viteCache = globalThis.__vite_ssr_module_cache__;
61+
if (typeof viteCache === "object" && viteCache !== null) {
62+
Object.keys(viteCache).forEach((key) => {
63+
if (key.includes("/main.tsx")) {
64+
delete viteCache[key];
65+
}
66+
});
67+
}
68+
const { mountApp } = await import("./main");
69+
expect(() => mountApp()).toThrow("Root element not found");
70+
});
71+
72+
it("calls createRoot with the root element", async () => {
73+
const { mountApp } = await import("./main");
74+
mountApp();
75+
expect(createRootMock).toHaveBeenCalledWith(rootElem);
76+
});
77+
78+
it("renders StrictMode > InstUISettingsProvider > App", async () => {
79+
const { mountApp } = await import("./main");
80+
mountApp();
81+
expect(renderSpy).toHaveBeenCalledTimes(1);
82+
83+
// Check the rendered tree structure
84+
const rendered = renderSpy.mock.calls[0][0];
85+
// Should be <StrictMode>
86+
expect(React.isValidElement(rendered)).toBe(true);
87+
expect(rendered.type).toBe(React.StrictMode);
88+
89+
// Should contain InstUISettingsProvider
90+
const provider = React.Children.only(rendered.props.children);
91+
expect(
92+
provider.type.name ||
93+
provider.type.displayName ||
94+
(typeof provider.type === "string" ? provider.type : ""),
95+
).toMatch(/InstUISettingsProvider/);
96+
97+
// Should contain App
98+
const app = React.Children.only(provider.props.children);
99+
expect(
100+
app.type.name ||
101+
app.type.displayName ||
102+
(typeof app.type === "string" ? app.type : ""),
103+
).toMatch(/App/);
104+
});
105+
});

apps/nutritionfacts/src/Main.tsx

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,27 @@ import { StrictMode } from "react";
33
import { createRoot, type Root } from "react-dom/client";
44
import App from "./App";
55

6-
const root: HTMLElement =
7-
document.getElementById("root") ||
8-
((): never => {
9-
throw new Error("Root element not found");
10-
})();
6+
const mountApp = () => {
7+
const foundRoot = document.getElementById("root");
8+
const root: HTMLElement =
9+
foundRoot ||
10+
(() => {
11+
throw new Error("Root element not found");
12+
})();
1113

12-
const appRoot: Root = createRoot(root);
14+
const appRoot: Root = createRoot(root);
1315

14-
appRoot.render(
15-
<StrictMode>
16-
<InstUISettingsProvider>
17-
<App />
18-
</InstUISettingsProvider>
19-
</StrictMode>,
20-
);
16+
appRoot.render(
17+
<StrictMode>
18+
<InstUISettingsProvider>
19+
<App />
20+
</InstUISettingsProvider>
21+
</StrictMode>,
22+
);
23+
};
24+
25+
if (typeof window !== "undefined" && import.meta.env?.MODE !== "test") {
26+
mountApp();
27+
}
28+
29+
export { mountApp };
Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
11
import baseConfig from "@instructure.ai/shared-configs/react";
22
import { defineConfig, mergeConfig } from "vite";
33

4-
export default mergeConfig(baseConfig, defineConfig({}));
4+
export default mergeConfig(
5+
baseConfig,
6+
defineConfig({
7+
define: {
8+
"import.meta.env.VITE_PACKAGE_VERSION": JSON.stringify(
9+
String(process.env.npm_package_version),
10+
),
11+
},
12+
}),
13+
);

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"browserslist": "^4.26.3",
1313
"istanbul-lib-report": "^3.0.1",
1414
"istanbul-reports": "^3.2.0",
15+
"jsdom": "^27.0.1",
1516
"lightningcss": "^1.30.2",
1617
"react-compiler-runtime": "^1.0.0",
1718
"terser": "^5.44.0",
@@ -22,7 +23,7 @@
2223
"vitest": "^3.2.4",
2324
"yaml": "^2.8.1"
2425
},
25-
"version": "1.5.0",
26+
"version": "1.5.1",
2627
"name": "@instructure.ai/shared-configs",
2728
"packageManager": "[email protected]",
2829
"private": true,

0 commit comments

Comments
 (0)