Skip to content

Commit 6b860fb

Browse files
committed
Merge remote-tracking branch 'origin/dev' into zachr/1883/exporter-ui
2 parents 835590f + 09a41ce commit 6b860fb

File tree

7 files changed

+185
-72
lines changed

7 files changed

+185
-72
lines changed

fission/bun.lock

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"react-icons": "^4.12.0",
2727
"three": "^0.178.0",
2828
"typescript-cookie": "^1.0.6",
29+
"vitest-browser-react": "^1.0.0",
2930
},
3031
"devDependencies": {
3132
"@emotion/react": "^11.14.0",
@@ -1519,6 +1520,8 @@
15191520

15201521
"vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="],
15211522

1523+
"vitest-browser-react": ["vitest-browser-react@1.0.0", "", { "peerDependencies": { "@types/react": "^18.0.0 || ^19.0.0", "@types/react-dom": "^18.0.0 || ^19.0.0", "@vitest/browser": "^2.1.0 || ^3.0.0 || ^4.0.0-0", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0", "vitest": "^2.1.0 || ^3.0.0 || ^4.0.0-0" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-d8oHuCOxG6KL6j2LOgUbU5Jh5ljPIqGoy38Pey1Fzw7BTWxYNsztNFmxbGNrBVSMcRcctDZXBSsgPIl9PLcD8Q=="],
1524+
15221525
"w3c-xmlserializer": ["w3c-xmlserializer@5.0.0", "", { "dependencies": { "xml-name-validator": "^5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="],
15231526

15241527
"webgl-constants": ["webgl-constants@1.1.1", "", {}, "sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg=="],

fission/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@
4242
"react-dom": "^18.3.1",
4343
"react-icons": "^4.12.0",
4444
"three": "^0.178.0",
45-
"typescript-cookie": "^1.0.6"
45+
"typescript-cookie": "^1.0.6",
46+
"vitest-browser-react": "^1.0.0"
4647
},
4748
"devDependencies": {
4849
"@emotion/react": "^11.14.0",

fission/src/Window.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
declare interface Window {
22
convertAuthToken(code: string): void
3+
gtag: () => void
34
}

fission/src/index.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
:root {
2424
/* font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; */
25-
font-family: "Artifakt";
25+
font-family: Artifakt, sans-serif;
2626
line-height: 1.5;
2727
font-weight: 400;
2828

fission/src/main.tsx

Lines changed: 1 addition & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,78 +1,9 @@
11
import ReactDOM from "react-dom/client"
22
import { ThemeProvider } from "@/ui/ThemeContext"
3-
import { Theme } from "@/ui/helpers/UseThemeHelpers"
43
import Synthesis from "./Synthesis"
54
import "./index.css"
65
import APS from "./aps/APS"
7-
8-
const initialThemeName = "Default"
9-
const defaultColors: Theme = {
10-
InteractiveElementSolid: {
11-
color: { r: 250, g: 162, b: 27, a: 1 },
12-
above: [],
13-
},
14-
InteractiveElementLeft: {
15-
color: { r: 207, g: 114, b: 57, a: 1 },
16-
above: ["Background", "BackgroundSecondary"],
17-
},
18-
InteractiveElementRight: {
19-
color: { r: 212, g: 75, b: 62, a: 1 },
20-
above: ["Background", "BackgroundSecondary"],
21-
},
22-
Background: { color: { r: 0, g: 0, b: 0, a: 1 }, above: [] },
23-
BackgroundSecondary: { color: { r: 18, g: 18, b: 18, a: 1 }, above: [] },
24-
InteractiveBackground: { color: { r: 40, g: 44, b: 47, a: 1 }, above: [] },
25-
MainText: {
26-
color: { r: 255, g: 255, b: 255, a: 1 },
27-
above: [
28-
"Background",
29-
"BackgroundSecondary",
30-
"BackgroundHUD",
31-
"InteractiveBackground",
32-
"InteractiveElementLeft",
33-
"InteractiveElementRight",
34-
],
35-
},
36-
Scrollbar: { color: { r: 170, g: 170, b: 170, a: 1 }, above: [] },
37-
AcceptButton: { color: { r: 33, g: 137, b: 228, a: 1 }, above: [] },
38-
CancelButton: { color: { r: 248, g: 78, b: 78, a: 1 }, above: [] },
39-
InteractiveElementText: {
40-
color: { r: 255, g: 255, b: 255, a: 1 },
41-
above: [],
42-
},
43-
AcceptCancelButtonText: {
44-
color: { r: 0, g: 0, b: 0, a: 1 },
45-
above: ["AcceptButton", "CancelButton"],
46-
},
47-
BackgroundHUD: { color: { r: 23, g: 23, b: 23, a: 1 }, above: [] },
48-
InteractiveHover: { color: { r: 150, g: 150, b: 150, a: 1 }, above: [] },
49-
InteractiveSelect: { color: { r: 100, g: 100, b: 100, a: 1 }, above: [] },
50-
Icon: {
51-
color: { r: 255, g: 255, b: 255, a: 1 },
52-
above: ["Background", "BackgroundSecondary", "InteractiveBackground"],
53-
},
54-
MainHUDIcon: {
55-
color: { r: 255, g: 255, b: 255, a: 1 },
56-
above: ["BackgroundHUD"],
57-
},
58-
MainHUDCloseIcon: {
59-
color: { r: 0, g: 0, b: 0, a: 1 },
60-
above: ["InteractiveElementRight", "#ffffff"],
61-
},
62-
HighlightHover: { color: { r: 89, g: 255, b: 133, a: 1 }, above: [] },
63-
HighlightSelect: { color: { r: 255, g: 89, b: 133, a: 1 }, above: [] },
64-
SkyboxTop: { color: { r: 255, g: 255, b: 255, a: 1 }, above: [] },
65-
SkyboxBottom: { color: { r: 255, g: 255, b: 255, a: 1 }, above: [] },
66-
FloorGrid: { color: { r: 93, g: 93, b: 93, a: 1 }, above: [] },
67-
MatchRedAlliance: { color: { r: 180, g: 20, b: 20, a: 1 }, above: [] },
68-
MatchBlueAlliance: { color: { r: 20, g: 20, b: 180, a: 1 }, above: [] },
69-
ToastInfo: { color: { r: 126, g: 34, b: 206, a: 1 }, above: [] },
70-
ToastWarning: { color: { r: 234, g: 179, b: 8, a: 1 }, above: [] },
71-
ToastError: { color: { r: 239, g: 68, b: 68, a: 1 }, above: [] },
72-
}
73-
const themes = {
74-
Default: defaultColors,
75-
}
6+
import { initialThemeName, themes, defaultColors } from "@/theme.ts"
767

778
window.convertAuthToken = code => APS.convertAuthToken(code)
789

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { afterAll, assert, beforeEach, describe, expect, expectTypeOf, test, vi } from "vitest"
2+
import { server } from "@vitest/browser/context"
3+
4+
import { cleanup, render, RenderResult } from "vitest-browser-react"
5+
import { ReactElement } from "react"
6+
import World from "@/systems/World.ts"
7+
8+
const { readFile } = server.commands
9+
10+
let screen: RenderResult | null
11+
const renderMock = vi.fn((children: ReactElement) => {
12+
screen = render(children)
13+
})
14+
vi.mock("react-dom/client", () => ({
15+
createRoot: vi.fn(() => ({ render: renderMock })),
16+
}))
17+
18+
describe("React Mounting", async () => {
19+
beforeEach(async () => {
20+
document.documentElement.innerHTML = await readFile("index.html")
21+
screen = null
22+
vi.resetAllMocks()
23+
})
24+
25+
afterAll(() => {
26+
cleanup()
27+
vi.resetModules()
28+
})
29+
30+
test("Root element exists", async () => {
31+
expect(document.getElementById("root")).not.toBeNull()
32+
})
33+
34+
test("Static stylesheets load", async () => {
35+
for (let i = 0; i < 50; i++) {
36+
await wait(200)
37+
if (document.styleSheets.length >= 2) {
38+
break
39+
}
40+
}
41+
42+
expect(document.styleSheets.length).toBe(2)
43+
const iterable = document.fonts.values()
44+
let iterator = iterable.next()
45+
let hasArtifaktFont = false
46+
while (!iterator.done) {
47+
if (iterator.value.family.includes("Artifakt")) {
48+
hasArtifaktFont = true
49+
break
50+
}
51+
iterator = iterable.next()
52+
}
53+
expect(hasArtifaktFont).toBeTruthy()
54+
})
55+
56+
// importing main.tsx has side effects that I could not clean up and can only be done once (per file),
57+
// so I am using one test and many annotations. It's possible that there's a better way, but I couldn't
58+
// find it in 4 hours of trying
59+
test(
60+
"App fully mounts through main.tsx",
61+
async ({ annotate, skip }) => {
62+
skip(server.browser == "firefox", "WebGL bug in Github Actions on Firefox")
63+
64+
await import("@/main.tsx")
65+
66+
expect(window.convertAuthToken).toBeDefined()
67+
expectTypeOf(window.convertAuthToken).toBeFunction()
68+
expect(window.gtag).toBeDefined()
69+
expectTypeOf(window.gtag).toBeFunction()
70+
await annotate("expected global functions mount")
71+
72+
// assorted style rules from index.css
73+
const style = window.getComputedStyle(document.body)
74+
expect(style.overflow).toBe("hidden")
75+
expect(style.overscrollBehavior).toBe("none")
76+
expect(style.fontFamily.split(",")[0].trim()).toBe("Artifakt")
77+
await annotate("index.css applied correctly")
78+
79+
expect(renderMock).toHaveBeenCalledOnce()
80+
assert(screen != null, "Screen was null")
81+
82+
const screenElement = screen.baseElement
83+
expect(screenElement.querySelector("canvas")).toBeInTheDocument()
84+
expect(screen.getByText("Singleplayer")).toBeInTheDocument()
85+
await annotate("DOM successfully updated to include Synthesis components")
86+
const initWorldSpy = vi.spyOn(World, "initWorld")
87+
await screen.getByText("Singleplayer").click()
88+
expect(initWorldSpy).toHaveBeenCalledOnce()
89+
await annotate("Singleplayer Button calls initWorld")
90+
91+
await wait(50)
92+
93+
await annotate("Initial Scene DOM", { contentType: "text/html", body: document.documentElement.outerHTML })
94+
95+
screen.unmount()
96+
97+
await annotate("Screen unmounted gracefully")
98+
},
99+
{ timeout: 20000 }
100+
)
101+
})
102+
103+
function wait(milliseconds: number) {
104+
return new Promise(resolve => {
105+
setTimeout(resolve, milliseconds)
106+
})
107+
}

fission/src/theme.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { Theme } from "@/ui/helpers/UseThemeHelpers.tsx"
2+
3+
export const initialThemeName = "Default"
4+
export const defaultColors: Theme = {
5+
InteractiveElementSolid: {
6+
color: { r: 250, g: 162, b: 27, a: 1 },
7+
above: [],
8+
},
9+
InteractiveElementLeft: {
10+
color: { r: 207, g: 114, b: 57, a: 1 },
11+
above: ["Background", "BackgroundSecondary"],
12+
},
13+
InteractiveElementRight: {
14+
color: { r: 212, g: 75, b: 62, a: 1 },
15+
above: ["Background", "BackgroundSecondary"],
16+
},
17+
Background: { color: { r: 0, g: 0, b: 0, a: 1 }, above: [] },
18+
BackgroundSecondary: { color: { r: 18, g: 18, b: 18, a: 1 }, above: [] },
19+
InteractiveBackground: { color: { r: 40, g: 44, b: 47, a: 1 }, above: [] },
20+
MainText: {
21+
color: { r: 255, g: 255, b: 255, a: 1 },
22+
above: [
23+
"Background",
24+
"BackgroundSecondary",
25+
"BackgroundHUD",
26+
"InteractiveBackground",
27+
"InteractiveElementLeft",
28+
"InteractiveElementRight",
29+
],
30+
},
31+
Scrollbar: { color: { r: 170, g: 170, b: 170, a: 1 }, above: [] },
32+
AcceptButton: { color: { r: 33, g: 137, b: 228, a: 1 }, above: [] },
33+
CancelButton: { color: { r: 248, g: 78, b: 78, a: 1 }, above: [] },
34+
InteractiveElementText: {
35+
color: { r: 255, g: 255, b: 255, a: 1 },
36+
above: [],
37+
},
38+
AcceptCancelButtonText: {
39+
color: { r: 0, g: 0, b: 0, a: 1 },
40+
above: ["AcceptButton", "CancelButton"],
41+
},
42+
BackgroundHUD: { color: { r: 23, g: 23, b: 23, a: 1 }, above: [] },
43+
InteractiveHover: { color: { r: 150, g: 150, b: 150, a: 1 }, above: [] },
44+
InteractiveSelect: { color: { r: 100, g: 100, b: 100, a: 1 }, above: [] },
45+
Icon: {
46+
color: { r: 255, g: 255, b: 255, a: 1 },
47+
above: ["Background", "BackgroundSecondary", "InteractiveBackground"],
48+
},
49+
MainHUDIcon: {
50+
color: { r: 255, g: 255, b: 255, a: 1 },
51+
above: ["BackgroundHUD"],
52+
},
53+
MainHUDCloseIcon: {
54+
color: { r: 0, g: 0, b: 0, a: 1 },
55+
above: ["InteractiveElementRight", "#ffffff"],
56+
},
57+
HighlightHover: { color: { r: 89, g: 255, b: 133, a: 1 }, above: [] },
58+
HighlightSelect: { color: { r: 255, g: 89, b: 133, a: 1 }, above: [] },
59+
SkyboxTop: { color: { r: 255, g: 255, b: 255, a: 1 }, above: [] },
60+
SkyboxBottom: { color: { r: 255, g: 255, b: 255, a: 1 }, above: [] },
61+
FloorGrid: { color: { r: 93, g: 93, b: 93, a: 1 }, above: [] },
62+
MatchRedAlliance: { color: { r: 180, g: 20, b: 20, a: 1 }, above: [] },
63+
MatchBlueAlliance: { color: { r: 20, g: 20, b: 180, a: 1 }, above: [] },
64+
ToastInfo: { color: { r: 126, g: 34, b: 206, a: 1 }, above: [] },
65+
ToastWarning: { color: { r: 234, g: 179, b: 8, a: 1 }, above: [] },
66+
ToastError: { color: { r: 239, g: 68, b: 68, a: 1 }, above: [] },
67+
}
68+
export const themes = {
69+
[initialThemeName]: defaultColors,
70+
}

0 commit comments

Comments
 (0)