mirror of
https://github.com/open-goal/launcher.git
synced 2024-10-19 14:47:36 -04:00
tests: Start writing tests, Splash and some of the lib/
functions (#280)
This commit is contained in:
parent
ab4b38c792
commit
620c41ceb4
33
.github/workflows/test.yaml
vendored
Normal file
33
.github/workflows/test.yaml
vendored
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
name: 🧪 Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
tags:
|
||||||
|
- v*
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
frontend:
|
||||||
|
name: Frontend
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 18
|
||||||
|
cache: yarn
|
||||||
|
|
||||||
|
- name: Install NPM Dependencies
|
||||||
|
run: yarn install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Run Tests
|
||||||
|
run: yarn test
|
||||||
|
|
||||||
|
# TODO - capture and report on coverage
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -41,3 +41,4 @@ TODO.md
|
||||||
vite.config.ts.timestamp*
|
vite.config.ts.timestamp*
|
||||||
.yarn/
|
.yarn/
|
||||||
.yarnrc.yml
|
.yarnrc.yml
|
||||||
|
coverage/
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
"test": "vitest",
|
||||||
|
"coverage": "vitest run --coverage",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"tauri": "tauri",
|
"tauri": "tauri",
|
||||||
|
@ -25,6 +27,8 @@
|
||||||
"@sveltejs/vite-plugin-svelte": "^2.4.2",
|
"@sveltejs/vite-plugin-svelte": "^2.4.2",
|
||||||
"@tauri-apps/cli": "^1.4.0",
|
"@tauri-apps/cli": "^1.4.0",
|
||||||
"@tauri-apps/tauricon": "github:tauri-apps/tauricon",
|
"@tauri-apps/tauricon": "github:tauri-apps/tauricon",
|
||||||
|
"@testing-library/svelte": "^4.0.3",
|
||||||
|
"@vitest/coverage-v8": "^0.33.0",
|
||||||
"@tsconfig/svelte": "^5.0.0",
|
"@tsconfig/svelte": "^5.0.0",
|
||||||
"ansi-to-span": "^0.0.1",
|
"ansi-to-span": "^0.0.1",
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.14",
|
||||||
|
@ -33,6 +37,7 @@
|
||||||
"execa": "^7.1.1",
|
"execa": "^7.1.1",
|
||||||
"flowbite": "^1.6.5",
|
"flowbite": "^1.6.5",
|
||||||
"flowbite-svelte": "^0.39.2",
|
"flowbite-svelte": "^0.39.2",
|
||||||
|
"jsdom": "^22.1.0",
|
||||||
"postcss": "^8.4.26",
|
"postcss": "^8.4.26",
|
||||||
"postcss-load-config": "^4.0.1",
|
"postcss-load-config": "^4.0.1",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "^2.8.8",
|
||||||
|
@ -42,6 +47,7 @@
|
||||||
"svelte-preprocess": "^5.0.3",
|
"svelte-preprocess": "^5.0.3",
|
||||||
"tailwindcss": "^3.3.3",
|
"tailwindcss": "^3.3.3",
|
||||||
"typescript": "^5.1.3",
|
"typescript": "^5.1.3",
|
||||||
|
"vitest": "^0.33.0",
|
||||||
"vite": "^4.4.4"
|
"vite": "^4.4.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
extractNewTexturePack,
|
extractNewTexturePack,
|
||||||
listExtractedTexturePackInfo,
|
listExtractedTexturePackInfo,
|
||||||
} from "$lib/rpc/features";
|
} from "$lib/rpc/features";
|
||||||
import { filePrompt } from "$lib/utils/file";
|
import { filePrompt } from "$lib/utils/file-dialogs";
|
||||||
import Icon from "@iconify/svelte";
|
import Icon from "@iconify/svelte";
|
||||||
import { convertFileSrc } from "@tauri-apps/api/tauri";
|
import { convertFileSrc } from "@tauri-apps/api/tauri";
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
runCompiler,
|
runCompiler,
|
||||||
runDecompiler,
|
runDecompiler,
|
||||||
} from "$lib/rpc/binaries";
|
} from "$lib/rpc/binaries";
|
||||||
import { folderPrompt, isoPrompt } from "$lib/utils/file";
|
import { folderPrompt, isoPrompt } from "$lib/utils/file-dialogs";
|
||||||
import {
|
import {
|
||||||
finalizeInstallation,
|
finalizeInstallation,
|
||||||
isAVXRequirementMet,
|
isAVXRequirementMet,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { saveFilePrompt } from "$lib/utils/file";
|
import { saveFilePrompt } from "$lib/utils/file-dialogs";
|
||||||
import { invoke_rpc } from "./rpc";
|
import { invoke_rpc } from "./rpc";
|
||||||
|
|
||||||
export async function generateSupportPackage(): Promise<void> {
|
export async function generateSupportPackage(): Promise<void> {
|
||||||
|
|
14
src/lib/utils/common.test.ts
Normal file
14
src/lib/utils/common.test.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { isInDebugMode } from "./common";
|
||||||
|
|
||||||
|
describe("isInDebugMode", () => {
|
||||||
|
it("should return true when in debug mode", async () => {
|
||||||
|
process.env["NODE_ENV"] = "development";
|
||||||
|
expect(isInDebugMode()).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true when in debug mode", async () => {
|
||||||
|
process.env["NODE_ENV"] = "not-development";
|
||||||
|
expect(isInDebugMode()).toBeFalsy();
|
||||||
|
});
|
||||||
|
});
|
284
src/lib/utils/github.test.ts
Normal file
284
src/lib/utils/github.test.ts
Normal file
|
@ -0,0 +1,284 @@
|
||||||
|
import { afterEach, describe, expect, it, vi, type Mock } from "vitest";
|
||||||
|
import { arch, platform } from "@tauri-apps/api/os";
|
||||||
|
import { listOfficialReleases } from "./github";
|
||||||
|
|
||||||
|
vi.mock("@tauri-apps/api/os");
|
||||||
|
global.fetch = vi.fn();
|
||||||
|
|
||||||
|
function createFetchResponse(data: any) {
|
||||||
|
return { json: () => new Promise((resolve) => resolve(data)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFakeGithubReleaseAsset(assetName) {
|
||||||
|
return {
|
||||||
|
url: "https://api.github.com/repos/open-goal/jak-project/releases/assets/115111791",
|
||||||
|
id: 115111791,
|
||||||
|
node_id: "RA_kwDOEUK6OM4G3Hdv",
|
||||||
|
name: assetName,
|
||||||
|
label: "",
|
||||||
|
uploader: {
|
||||||
|
login: "github-actions[bot]",
|
||||||
|
id: 41898282,
|
||||||
|
node_id: "MDM6Qm90NDE4OTgyODI=",
|
||||||
|
avatar_url: "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||||
|
gravatar_id: "",
|
||||||
|
url: "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||||
|
html_url: "https://github.com/apps/github-actions",
|
||||||
|
followers_url:
|
||||||
|
"https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||||
|
following_url:
|
||||||
|
"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||||
|
gists_url:
|
||||||
|
"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||||
|
starred_url:
|
||||||
|
"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||||
|
subscriptions_url:
|
||||||
|
"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||||
|
organizations_url:
|
||||||
|
"https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||||
|
repos_url: "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||||
|
events_url:
|
||||||
|
"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||||
|
received_events_url:
|
||||||
|
"https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||||
|
type: "Bot",
|
||||||
|
site_admin: false,
|
||||||
|
},
|
||||||
|
content_type: "application/x-gtar",
|
||||||
|
state: "uploaded",
|
||||||
|
size: 21698082,
|
||||||
|
download_count: 399,
|
||||||
|
created_at: "2023-07-01T06:38:26Z",
|
||||||
|
updated_at: "2023-07-01T06:38:28Z",
|
||||||
|
browser_download_url: `https://github.com/open-goal/jak-project/releases/download/v0.1.38/${assetName}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFakeGithubRelease(assetNames: string[]) {
|
||||||
|
const assets = [];
|
||||||
|
for (const assetName of assetNames) {
|
||||||
|
assets.push(createFakeGithubReleaseAsset(assetName));
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
url: "https://api.github.com/repos/open-goal/jak-project/releases/110648537",
|
||||||
|
assets_url:
|
||||||
|
"https://api.github.com/repos/open-goal/jak-project/releases/110648537/assets",
|
||||||
|
upload_url:
|
||||||
|
"https://uploads.github.com/repos/open-goal/jak-project/releases/110648537/assets{?name,label}",
|
||||||
|
html_url: "https://github.com/open-goal/jak-project/releases/tag/v0.1.38",
|
||||||
|
id: 110648537,
|
||||||
|
author: {
|
||||||
|
login: "OpenGOALBot",
|
||||||
|
id: 99294829,
|
||||||
|
node_id: "U_kgDOBesebQ",
|
||||||
|
avatar_url: "https://avatars.githubusercontent.com/u/99294829?v=4",
|
||||||
|
gravatar_id: "",
|
||||||
|
url: "https://api.github.com/users/OpenGOALBot",
|
||||||
|
html_url: "https://github.com/OpenGOALBot",
|
||||||
|
followers_url: "https://api.github.com/users/OpenGOALBot/followers",
|
||||||
|
following_url:
|
||||||
|
"https://api.github.com/users/OpenGOALBot/following{/other_user}",
|
||||||
|
gists_url: "https://api.github.com/users/OpenGOALBot/gists{/gist_id}",
|
||||||
|
starred_url:
|
||||||
|
"https://api.github.com/users/OpenGOALBot/starred{/owner}{/repo}",
|
||||||
|
subscriptions_url:
|
||||||
|
"https://api.github.com/users/OpenGOALBot/subscriptions",
|
||||||
|
organizations_url: "https://api.github.com/users/OpenGOALBot/orgs",
|
||||||
|
repos_url: "https://api.github.com/users/OpenGOALBot/repos",
|
||||||
|
events_url: "https://api.github.com/users/OpenGOALBot/events{/privacy}",
|
||||||
|
received_events_url:
|
||||||
|
"https://api.github.com/users/OpenGOALBot/received_events",
|
||||||
|
type: "User",
|
||||||
|
site_admin: false,
|
||||||
|
},
|
||||||
|
node_id: "RE_kwDOEUK6OM4GmFzZ",
|
||||||
|
tag_name: "v0.1.38",
|
||||||
|
target_commitish: "master",
|
||||||
|
name: "v0.1.38",
|
||||||
|
draft: false,
|
||||||
|
prerelease: false,
|
||||||
|
created_at: "2023-07-01T06:09:09Z",
|
||||||
|
published_at: "2023-07-01T06:38:29Z",
|
||||||
|
assets: assets,
|
||||||
|
tarball_url:
|
||||||
|
"https://api.github.com/repos/open-goal/jak-project/tarball/v0.1.38",
|
||||||
|
zipball_url:
|
||||||
|
"https://api.github.com/repos/open-goal/jak-project/zipball/v0.1.38",
|
||||||
|
body: "## What's Changed\n* ci: ensure linux runners have the proper OpenGL headers by @xTVaser in https://github.com/open-goal/jak-project/pull/2790\n\n\n**Full Changelog**: https://github.com/open-goal/jak-project/compare/v0.1.37...v0.1.38",
|
||||||
|
reactions: {
|
||||||
|
url: "https://api.github.com/repos/open-goal/jak-project/releases/110648537/reactions",
|
||||||
|
total_count: 4,
|
||||||
|
"+1": 0,
|
||||||
|
"-1": 0,
|
||||||
|
laugh: 0,
|
||||||
|
hooray: 3,
|
||||||
|
confused: 0,
|
||||||
|
heart: 0,
|
||||||
|
rocket: 0,
|
||||||
|
eyes: 1,
|
||||||
|
},
|
||||||
|
mentions_count: 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("listOfficialReleases", () => {
|
||||||
|
afterEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
vi.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should retrieve intel macOS releases properly", async () => {
|
||||||
|
vi.mocked(platform).mockResolvedValue("darwin");
|
||||||
|
vi.mocked(arch).mockResolvedValue("x86_64");
|
||||||
|
(fetch as Mock).mockResolvedValue(
|
||||||
|
createFetchResponse([
|
||||||
|
createFakeGithubRelease([
|
||||||
|
"opengoal-macos-intel-v0.0.1.tar.gz",
|
||||||
|
"opengoal-windows-v0.0.1.zip",
|
||||||
|
"opengoal-linux-v0.0.1.tar.gz",
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
const releases = await listOfficialReleases();
|
||||||
|
expect(releases.length).toBe(1);
|
||||||
|
expect(
|
||||||
|
releases[0].downloadUrl.endsWith("opengoal-macos-intel-v0.0.1.tar.gz")
|
||||||
|
).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not retrieve macOS ARM releases", async () => {
|
||||||
|
vi.mocked(platform).mockResolvedValue("darwin");
|
||||||
|
vi.mocked(arch).mockResolvedValue("arm");
|
||||||
|
(fetch as Mock).mockResolvedValue(
|
||||||
|
createFetchResponse([
|
||||||
|
createFakeGithubRelease([
|
||||||
|
"opengoal-macos-intel-v0.0.1.tar.gz",
|
||||||
|
"opengoal-windows-v0.0.1.zip",
|
||||||
|
"opengoal-linux-v0.0.1.tar.gz",
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
const releases = await listOfficialReleases();
|
||||||
|
expect(releases.length).toBe(1);
|
||||||
|
expect(releases[0].downloadUrl).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should retrieve windows releases properly", async () => {
|
||||||
|
vi.mocked(platform).mockResolvedValue("win32");
|
||||||
|
vi.mocked(arch).mockResolvedValue("x86_64");
|
||||||
|
(fetch as Mock).mockResolvedValue(
|
||||||
|
createFetchResponse([
|
||||||
|
createFakeGithubRelease([
|
||||||
|
"opengoal-macos-intel-v0.0.1.tar.gz",
|
||||||
|
"opengoal-windows-v0.0.1.zip",
|
||||||
|
"opengoal-linux-v0.0.1.tar.gz",
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
const releases = await listOfficialReleases();
|
||||||
|
expect(releases.length).toBe(1);
|
||||||
|
expect(
|
||||||
|
releases[0].downloadUrl.endsWith("opengoal-windows-v0.0.1.zip")
|
||||||
|
).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should retrieve linux releases properly", async () => {
|
||||||
|
vi.mocked(platform).mockResolvedValue("linux");
|
||||||
|
vi.mocked(arch).mockResolvedValue("x86_64");
|
||||||
|
(fetch as Mock).mockResolvedValue(
|
||||||
|
createFetchResponse([
|
||||||
|
createFakeGithubRelease([
|
||||||
|
"opengoal-macos-intel-v0.0.1.tar.gz",
|
||||||
|
"opengoal-windows-v0.0.1.zip",
|
||||||
|
"opengoal-linux-v0.0.1.tar.gz",
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
const releases = await listOfficialReleases();
|
||||||
|
expect(releases.length).toBe(1);
|
||||||
|
expect(
|
||||||
|
releases[0].downloadUrl.endsWith("opengoal-linux-v0.0.1.tar.gz")
|
||||||
|
).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("getLatestOfficialRelease", () => {
|
||||||
|
afterEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
vi.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should retrieve intel macOS releases properly", async () => {
|
||||||
|
vi.mocked(platform).mockResolvedValue("darwin");
|
||||||
|
vi.mocked(arch).mockResolvedValue("x86_64");
|
||||||
|
(fetch as Mock).mockResolvedValue(
|
||||||
|
createFetchResponse([
|
||||||
|
createFakeGithubRelease([
|
||||||
|
"opengoal-macos-intel-v0.0.1.tar.gz",
|
||||||
|
"opengoal-windows-v0.0.1.zip",
|
||||||
|
"opengoal-linux-v0.0.1.tar.gz",
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
const releases = await listOfficialReleases();
|
||||||
|
expect(releases.length).toBe(1);
|
||||||
|
expect(
|
||||||
|
releases[0].downloadUrl.endsWith("opengoal-macos-intel-v0.0.1.tar.gz")
|
||||||
|
).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not retrieve macOS ARM releases", async () => {
|
||||||
|
vi.mocked(platform).mockResolvedValue("darwin");
|
||||||
|
vi.mocked(arch).mockResolvedValue("arm");
|
||||||
|
(fetch as Mock).mockResolvedValue(
|
||||||
|
createFetchResponse([
|
||||||
|
createFakeGithubRelease([
|
||||||
|
"opengoal-macos-intel-v0.0.1.tar.gz",
|
||||||
|
"opengoal-windows-v0.0.1.zip",
|
||||||
|
"opengoal-linux-v0.0.1.tar.gz",
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
const releases = await listOfficialReleases();
|
||||||
|
expect(releases.length).toBe(1);
|
||||||
|
expect(releases[0].downloadUrl).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should retrieve windows releases properly", async () => {
|
||||||
|
vi.mocked(platform).mockResolvedValue("win32");
|
||||||
|
vi.mocked(arch).mockResolvedValue("x86_64");
|
||||||
|
(fetch as Mock).mockResolvedValue(
|
||||||
|
createFetchResponse([
|
||||||
|
createFakeGithubRelease([
|
||||||
|
"opengoal-macos-intel-v0.0.1.tar.gz",
|
||||||
|
"opengoal-windows-v0.0.1.zip",
|
||||||
|
"opengoal-linux-v0.0.1.tar.gz",
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
const releases = await listOfficialReleases();
|
||||||
|
expect(releases.length).toBe(1);
|
||||||
|
expect(
|
||||||
|
releases[0].downloadUrl.endsWith("opengoal-windows-v0.0.1.zip")
|
||||||
|
).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should retrieve linux releases properly", async () => {
|
||||||
|
vi.mocked(platform).mockResolvedValue("linux");
|
||||||
|
vi.mocked(arch).mockResolvedValue("x86_64");
|
||||||
|
(fetch as Mock).mockResolvedValue(
|
||||||
|
createFetchResponse([
|
||||||
|
createFakeGithubRelease([
|
||||||
|
"opengoal-macos-intel-v0.0.1.tar.gz",
|
||||||
|
"opengoal-windows-v0.0.1.zip",
|
||||||
|
"opengoal-linux-v0.0.1.tar.gz",
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
);
|
||||||
|
const releases = await listOfficialReleases();
|
||||||
|
expect(releases.length).toBe(1);
|
||||||
|
expect(
|
||||||
|
releases[0].downloadUrl.endsWith("opengoal-linux-v0.0.1.tar.gz")
|
||||||
|
).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -10,30 +10,54 @@ export interface ReleaseInfo {
|
||||||
pendingAction: boolean;
|
pendingAction: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isIntelMacOsRelease(
|
||||||
|
platform: string,
|
||||||
|
architecture: string,
|
||||||
|
assetName: string
|
||||||
|
): boolean {
|
||||||
|
return (
|
||||||
|
platform === "darwin" &&
|
||||||
|
architecture === "x86_64" &&
|
||||||
|
assetName.startsWith("opengoal-macos-intel-v")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO - go back and fix old asset names so windows/linux can be simplified
|
||||||
|
function isWindowsRelease(
|
||||||
|
platform: string,
|
||||||
|
architecture: string,
|
||||||
|
assetName: string
|
||||||
|
): boolean {
|
||||||
|
return (
|
||||||
|
platform === "win32" &&
|
||||||
|
(assetName.startsWith("opengoal-windows-v") ||
|
||||||
|
(assetName.startsWith("opengoal-v") && assetName.includes("windows")))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isLinuxRelease(
|
||||||
|
platform: string,
|
||||||
|
architecture: string,
|
||||||
|
assetName: string
|
||||||
|
): boolean {
|
||||||
|
return (
|
||||||
|
platform === "linux" &&
|
||||||
|
(assetName.startsWith("opengoal-linux-v") ||
|
||||||
|
(assetName.startsWith("opengoal-v") && assetName.includes("linux")))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async function getDownloadLinkForCurrentPlatform(
|
async function getDownloadLinkForCurrentPlatform(
|
||||||
release
|
release: any
|
||||||
): Promise<string | undefined> {
|
): Promise<string | undefined> {
|
||||||
const platformName = await platform();
|
const platformName = await platform();
|
||||||
const archName = await arch();
|
const archName = await arch();
|
||||||
for (const asset of release.assets) {
|
for (const asset of release.assets) {
|
||||||
if (
|
if (isIntelMacOsRelease(platformName, archName, asset.name)) {
|
||||||
platformName === "darwin" &&
|
|
||||||
archName === "x86_64" &&
|
|
||||||
asset.name.startsWith("opengoal-macos-intel-v")
|
|
||||||
// macOS doesn't have the old naming scheme
|
|
||||||
) {
|
|
||||||
return asset.browser_download_url;
|
return asset.browser_download_url;
|
||||||
} else if (
|
} else if (isWindowsRelease(platformName, archName, asset.name)) {
|
||||||
platformName === "win32" &&
|
|
||||||
(asset.name.startsWith("opengoal-windows-v") ||
|
|
||||||
(asset.name.startsWith("opengoal-v") && asset.name.includes("windows")))
|
|
||||||
) {
|
|
||||||
return asset.browser_download_url;
|
return asset.browser_download_url;
|
||||||
} else if (
|
} else if (isLinuxRelease(platformName, archName, asset.name)) {
|
||||||
platformName === "linux" &&
|
|
||||||
(asset.name.startsWith("opengoal-linux-v") ||
|
|
||||||
(asset.name.startsWith("opengoal-v") && asset.name.includes("linux")))
|
|
||||||
) {
|
|
||||||
return asset.browser_download_url;
|
return asset.browser_download_url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +68,6 @@ export async function listOfficialReleases(): Promise<ReleaseInfo[]> {
|
||||||
let releases = [];
|
let releases = [];
|
||||||
// TODO - handle rate limiting
|
// TODO - handle rate limiting
|
||||||
// TODO - long term - handle pagination (more than 100 releases)
|
// TODO - long term - handle pagination (more than 100 releases)
|
||||||
// TODO - even longer term - extract this out into an API we control (avoid github rate limiting) -- will be needed for unofficial releases as well anyway
|
|
||||||
const resp = await fetch(
|
const resp = await fetch(
|
||||||
"https://api.github.com/repos/open-goal/jak-project/releases?per_page=100"
|
"https://api.github.com/repos/open-goal/jak-project/releases?per_page=100"
|
||||||
);
|
);
|
||||||
|
@ -68,7 +91,6 @@ export async function listOfficialReleases(): Promise<ReleaseInfo[]> {
|
||||||
|
|
||||||
export async function getLatestOfficialRelease(): Promise<ReleaseInfo> {
|
export async function getLatestOfficialRelease(): Promise<ReleaseInfo> {
|
||||||
// TODO - handle rate limiting
|
// TODO - handle rate limiting
|
||||||
// TODO - even longer term - extract this out into an API we control (avoid github rate limiting) -- will be needed for unofficial releases as well anyway
|
|
||||||
const resp = await fetch(
|
const resp = await fetch(
|
||||||
"https://api.github.com/repos/open-goal/jak-project/releases/latest"
|
"https://api.github.com/repos/open-goal/jak-project/releases/latest"
|
||||||
);
|
);
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
setInstallationDirectory,
|
setInstallationDirectory,
|
||||||
} from "$lib/rpc/config";
|
} from "$lib/rpc/config";
|
||||||
import { VersionStore } from "$lib/stores/VersionStore";
|
import { VersionStore } from "$lib/stores/VersionStore";
|
||||||
import { folderPrompt } from "$lib/utils/file";
|
import { folderPrompt } from "$lib/utils/file-dialogs";
|
||||||
import { Label, Input } from "flowbite-svelte";
|
import { Label, Input } from "flowbite-svelte";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import { _ } from "svelte-i18n";
|
import { _ } from "svelte-i18n";
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import { openMainWindow } from "$lib/rpc/window";
|
import { openMainWindow } from "$lib/rpc/window";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import logo from "$assets/images/icon.webp";
|
import logo from "$assets/images/icon.webp";
|
||||||
import { folderPrompt } from "$lib/utils/file";
|
import { folderPrompt } from "$lib/utils/file-dialogs";
|
||||||
import {
|
import {
|
||||||
deleteOldDataDirectory,
|
deleteOldDataDirectory,
|
||||||
getInstallationDirectory,
|
getInstallationDirectory,
|
||||||
|
@ -12,10 +12,10 @@
|
||||||
setLocale,
|
setLocale,
|
||||||
} from "$lib/rpc/config";
|
} from "$lib/rpc/config";
|
||||||
import { AVAILABLE_LOCALES } from "$lib/i18n/i18n";
|
import { AVAILABLE_LOCALES } from "$lib/i18n/i18n";
|
||||||
import { _ } from "svelte-i18n";
|
import { locale as svelteLocale, _ } from "svelte-i18n";
|
||||||
|
|
||||||
let currentProgress = 10;
|
let currentProgress = 10;
|
||||||
let currentStatusText = $_("splash_step_readingSettings");
|
let currentStatusText = "Loading Locales...";
|
||||||
|
|
||||||
let selectLocale = false;
|
let selectLocale = false;
|
||||||
let installationDirSet = true;
|
let installationDirSet = true;
|
||||||
|
@ -26,6 +26,7 @@
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
// First, see if the user has selected a locale
|
// First, see if the user has selected a locale
|
||||||
await checkLocale();
|
await checkLocale();
|
||||||
|
currentStatusText = $_("splash_step_readingSettings");
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO - cleanup this code and make it easier to add steps like with
|
// TODO - cleanup this code and make it easier to add steps like with
|
||||||
|
@ -36,6 +37,7 @@
|
||||||
if (locale === null) {
|
if (locale === null) {
|
||||||
// Prompt the user to select a locale
|
// Prompt the user to select a locale
|
||||||
selectLocale = true;
|
selectLocale = true;
|
||||||
|
svelteLocale.set("en-US");
|
||||||
} else {
|
} else {
|
||||||
// Set locale and continue
|
// Set locale and continue
|
||||||
setLocale(locale);
|
setLocale(locale);
|
||||||
|
@ -74,25 +76,35 @@
|
||||||
currentStatusText = $_("splash_step_errorOpening");
|
currentStatusText = $_("splash_step_errorOpening");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleLocaleChange(evt: Event) {
|
||||||
|
const selectElement = evt.target as HTMLSelectElement;
|
||||||
|
setLocale(selectElement.value);
|
||||||
|
selectLocale = false;
|
||||||
|
await checkDirectories();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="content" data-tauri-drag-region>
|
<div class="content" data-tauri-drag-region>
|
||||||
<div class="splash-logo no-pointer-events">
|
<div class="splash-logo no-pointer-events">
|
||||||
<img src={logo} aria-label="" draggable="false" />
|
<img
|
||||||
|
src={logo}
|
||||||
|
data-testId="splash-logo"
|
||||||
|
alt="OpenGOAL logo"
|
||||||
|
aria-label="OpenGOAL logo"
|
||||||
|
draggable="false"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="splash-contents no-pointer-events">
|
<div class="splash-contents no-pointer-events">
|
||||||
{#if selectLocale}
|
{#if selectLocale}
|
||||||
<span class="mb-1">{$_("splash_selectLocale")}</span>
|
<span class="mb-1">{$_("splash_selectLocale")}</span>
|
||||||
<div class="splash-select">
|
<div class="splash-select">
|
||||||
<select
|
<select
|
||||||
name="cars"
|
data-testId="locale-select"
|
||||||
id="cars"
|
name="locales"
|
||||||
|
id="locales"
|
||||||
class="pointer-events emoji-font"
|
class="pointer-events emoji-font"
|
||||||
on:change={async (evt) => {
|
on:change={handleLocaleChange}
|
||||||
setLocale(evt.target.value);
|
|
||||||
selectLocale = false;
|
|
||||||
await checkDirectories();
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<option disabled selected value hidden />
|
<option disabled selected value hidden />
|
||||||
{#each AVAILABLE_LOCALES as locale}
|
{#each AVAILABLE_LOCALES as locale}
|
||||||
|
@ -107,6 +119,7 @@
|
||||||
<br />
|
<br />
|
||||||
<span>
|
<span>
|
||||||
<button
|
<button
|
||||||
|
data-testId="delete-old-data-dir-button"
|
||||||
class="splash-button pointer-events"
|
class="splash-button pointer-events"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
oldDataDirToClean = false;
|
oldDataDirToClean = false;
|
||||||
|
@ -114,6 +127,7 @@
|
||||||
}}>{$_("splash_button_deleteOldInstallDir_yes")}</button
|
}}>{$_("splash_button_deleteOldInstallDir_yes")}</button
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
|
data-testId="dont-delete-old-data-dir-button"
|
||||||
class="splash-button pointer-events"
|
class="splash-button pointer-events"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
oldDataDirToClean = false;
|
oldDataDirToClean = false;
|
||||||
|
@ -128,6 +142,7 @@
|
||||||
{/if}
|
{/if}
|
||||||
<br />
|
<br />
|
||||||
<button
|
<button
|
||||||
|
data-testId="pick-install-folder-button"
|
||||||
class="splash-button pointer-events"
|
class="splash-button pointer-events"
|
||||||
on:click={async () => {
|
on:click={async () => {
|
||||||
// This is part of what allows for the user to install the games and such wherever they want
|
// This is part of what allows for the user to install the games and such wherever they want
|
||||||
|
|
198
src/splash/splash.test.ts
Normal file
198
src/splash/splash.test.ts
Normal file
|
@ -0,0 +1,198 @@
|
||||||
|
import {
|
||||||
|
render,
|
||||||
|
waitFor,
|
||||||
|
screen,
|
||||||
|
cleanup,
|
||||||
|
fireEvent,
|
||||||
|
} from "@testing-library/svelte";
|
||||||
|
import Splash from "./Splash.svelte";
|
||||||
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import { mockIPC } from "@tauri-apps/api/mocks";
|
||||||
|
import { folderPrompt } from "$lib/utils/file-dialogs";
|
||||||
|
|
||||||
|
vi.mock("$lib/utils/file-dialogs");
|
||||||
|
|
||||||
|
describe("Splash.svelte", () => {
|
||||||
|
// TODO: @testing-library/svelte claims to add this automatically but it doesn't work without explicit afterEach
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup();
|
||||||
|
vi.clearAllMocks();
|
||||||
|
vi.resetAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should render the splash", async () => {
|
||||||
|
render(Splash, {});
|
||||||
|
const logo = screen.getByTestId("splash-logo");
|
||||||
|
expect(logo).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should display the locale dropdown", async () => {
|
||||||
|
// TODO - generalize into function
|
||||||
|
mockIPC((cmd, args) => {
|
||||||
|
if (cmd === "get_locale") {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
console.log(`Unhandled Tauri IPC: ${cmd}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
render(Splash, {});
|
||||||
|
const localeSelect = await screen.findByTestId("locale-select");
|
||||||
|
expect(localeSelect).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should set the locale when selected", async () => {
|
||||||
|
// TODO - generalize into function
|
||||||
|
mockIPC((cmd, args) => {
|
||||||
|
if (cmd === "get_locale") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (cmd === "set_locale") {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
console.log(`Unhandled Tauri IPC: ${cmd}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
render(Splash, {});
|
||||||
|
const localeSelect = (await screen.findByTestId(
|
||||||
|
"locale-select"
|
||||||
|
)) as HTMLSelectElement;
|
||||||
|
expect(localeSelect).toBeTruthy();
|
||||||
|
fireEvent.change(localeSelect, { target: { value: "en-US" } });
|
||||||
|
expect(localeSelect.value).toBe("en-US");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should prompt user to delete old data directory - delete it", async () => {
|
||||||
|
// TODO - generalize into function
|
||||||
|
// return an object that tracks mock calls / args
|
||||||
|
let oldDataDirDeleted = false;
|
||||||
|
mockIPC((cmd, args) => {
|
||||||
|
if (cmd === "get_locale") {
|
||||||
|
return "en-US";
|
||||||
|
} else if (cmd === "get_install_directory") {
|
||||||
|
return null;
|
||||||
|
} else if (cmd === "has_old_data_directory") {
|
||||||
|
return true;
|
||||||
|
} else if (cmd === "delete_old_data_directory") {
|
||||||
|
oldDataDirDeleted = true;
|
||||||
|
} else {
|
||||||
|
console.log(`Unhandled Tauri IPC: ${cmd}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
render(Splash, {});
|
||||||
|
const deleteOldDataDirButton = await screen.findByTestId(
|
||||||
|
"delete-old-data-dir-button"
|
||||||
|
);
|
||||||
|
expect(deleteOldDataDirButton).toBeTruthy();
|
||||||
|
// delete the dir, it'll go away
|
||||||
|
fireEvent.click(deleteOldDataDirButton);
|
||||||
|
expect(oldDataDirDeleted).toBeTruthy();
|
||||||
|
const pickInstallFolderButton = await screen.findByTestId(
|
||||||
|
"pick-install-folder-button"
|
||||||
|
);
|
||||||
|
expect(pickInstallFolderButton).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should prompt user to select installation directory - cancelled dialog", async () => {
|
||||||
|
// TODO - generalize into function
|
||||||
|
// return an object that tracks mock calls / args
|
||||||
|
mockIPC((cmd, args) => {
|
||||||
|
if (cmd === "get_locale") {
|
||||||
|
return "en-US";
|
||||||
|
} else if (cmd === "get_install_directory") {
|
||||||
|
return null;
|
||||||
|
} else if (cmd === "has_old_data_directory") {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
console.log(`Unhandled Tauri IPC: ${cmd}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
vi.mocked(folderPrompt).mockResolvedValue(undefined);
|
||||||
|
render(Splash, {});
|
||||||
|
let pickInstallFolderButton = await screen.findByTestId(
|
||||||
|
"pick-install-folder-button"
|
||||||
|
);
|
||||||
|
expect(pickInstallFolderButton).toBeTruthy();
|
||||||
|
fireEvent.click(pickInstallFolderButton);
|
||||||
|
// It's still there since the user didn't pick a folder
|
||||||
|
pickInstallFolderButton = await screen.findByTestId(
|
||||||
|
"pick-install-folder-button"
|
||||||
|
);
|
||||||
|
expect(pickInstallFolderButton).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should prompt user to select installation directory - successful dialog", async () => {
|
||||||
|
// TODO - generalize into function
|
||||||
|
// return an object that tracks mock calls / args
|
||||||
|
let setInstallDirectorySet = false;
|
||||||
|
let mainWindowOpened = false;
|
||||||
|
mockIPC((cmd, args) => {
|
||||||
|
if (cmd === "get_locale") {
|
||||||
|
return "en-US";
|
||||||
|
} else if (cmd === "get_install_directory") {
|
||||||
|
return null;
|
||||||
|
} else if (cmd === "has_old_data_directory") {
|
||||||
|
return false;
|
||||||
|
} else if (cmd === "set_install_directory") {
|
||||||
|
setInstallDirectorySet = true;
|
||||||
|
return null;
|
||||||
|
} else if (cmd === "open_main_window") {
|
||||||
|
mainWindowOpened = true;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
console.log(`Unhandled Tauri IPC: ${cmd}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
vi.mocked(folderPrompt).mockResolvedValue("/wow/good/job/nice/folder");
|
||||||
|
render(Splash, {});
|
||||||
|
let pickInstallFolderButton = await screen.findByTestId(
|
||||||
|
"pick-install-folder-button"
|
||||||
|
);
|
||||||
|
expect(pickInstallFolderButton).toBeTruthy();
|
||||||
|
fireEvent.click(pickInstallFolderButton);
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(setInstallDirectorySet).toBeTruthy();
|
||||||
|
});
|
||||||
|
await waitFor(
|
||||||
|
() => {
|
||||||
|
expect(mainWindowOpened).toBeTruthy();
|
||||||
|
},
|
||||||
|
{ timeout: 5000 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should prompt user to select installation directory - bad directory choosen", async () => {
|
||||||
|
// TODO - generalize into function
|
||||||
|
// return an object that tracks mock calls / args
|
||||||
|
let mainWindowOpened = false;
|
||||||
|
mockIPC((cmd, args) => {
|
||||||
|
if (cmd === "get_locale") {
|
||||||
|
return "en-US";
|
||||||
|
} else if (cmd === "get_install_directory") {
|
||||||
|
return null;
|
||||||
|
} else if (cmd === "has_old_data_directory") {
|
||||||
|
return false;
|
||||||
|
} else if (cmd === "set_install_directory") {
|
||||||
|
return "wow that was a terrible directory";
|
||||||
|
} else if (cmd === "open_main_window") {
|
||||||
|
mainWindowOpened = true;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
console.log(`Unhandled Tauri IPC: ${cmd}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
vi.mocked(folderPrompt).mockResolvedValue("/wow/good/job/nice/folder");
|
||||||
|
render(Splash, {});
|
||||||
|
let pickInstallFolderButton = await screen.findByTestId(
|
||||||
|
"pick-install-folder-button"
|
||||||
|
);
|
||||||
|
expect(pickInstallFolderButton).toBeTruthy();
|
||||||
|
fireEvent.click(pickInstallFolderButton);
|
||||||
|
await waitFor(() => {
|
||||||
|
screen.findByText("wow that was a terrible directory");
|
||||||
|
});
|
||||||
|
pickInstallFolderButton = await screen.findByTestId(
|
||||||
|
"pick-install-folder-button"
|
||||||
|
);
|
||||||
|
expect(pickInstallFolderButton).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
5
src/tests/setup.ts
Normal file
5
src/tests/setup.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
import { vi, beforeAll } from "vitest";
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
window.__TAURI_IPC__ = vi.fn();
|
||||||
|
});
|
|
@ -24,7 +24,8 @@
|
||||||
* Typecheck JS in `.svelte` and `.js` files by default.
|
* Typecheck JS in `.svelte` and `.js` files by default.
|
||||||
* Disable this if you'd like to use dynamic types.
|
* Disable this if you'd like to use dynamic types.
|
||||||
*/
|
*/
|
||||||
"checkJs": true
|
"checkJs": true,
|
||||||
|
"types": ["vitest/types"]
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Use global.d.ts instead of compilerOptions.types
|
* Use global.d.ts instead of compilerOptions.types
|
||||||
|
|
|
@ -7,7 +7,7 @@ export default defineConfig({
|
||||||
server: {
|
server: {
|
||||||
port: 3000, // The port the server will listen on.
|
port: 3000, // The port the server will listen on.
|
||||||
},
|
},
|
||||||
plugins: [svelte()],
|
plugins: [svelte({ hot: !process.env.VITEST })],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
$lib: fileURLToPath(new URL("./src/lib", import.meta.url)),
|
$lib: fileURLToPath(new URL("./src/lib", import.meta.url)),
|
||||||
|
|
20
vitest.config.ts
Normal file
20
vitest.config.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import { defineConfig } from "vitest/config";
|
||||||
|
import { svelte } from "@sveltejs/vite-plugin-svelte";
|
||||||
|
import { fileURLToPath, URL } from "url";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [svelte({ hot: !process.env.VITEST })],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
$lib: fileURLToPath(new URL("./src/lib", import.meta.url)),
|
||||||
|
$assets: fileURLToPath(new URL("./src/assets", import.meta.url)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
test: {
|
||||||
|
alias: [{ find: /^svelte$/, replacement: "svelte/internal" }],
|
||||||
|
include: ["./src/**/*.test.ts"],
|
||||||
|
globals: true,
|
||||||
|
environment: "jsdom",
|
||||||
|
setupFiles: ["./src/tests/setup.ts"],
|
||||||
|
},
|
||||||
|
});
|
Loading…
Reference in a new issue