Skip to content

Commit

Permalink
TS, JS test runners
Browse files Browse the repository at this point in the history
  • Loading branch information
x0k committed Jun 3, 2024
1 parent a6d663b commit 1e6a3df
Show file tree
Hide file tree
Showing 19 changed files with 306 additions and 109 deletions.
3 changes: 3 additions & 0 deletions mkfilex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
mk:
bun run dev

c:
bunx astro check

f:
bunx prettier --write .

Expand Down
3 changes: 2 additions & 1 deletion src/adapters/monaco.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Language } from "@/lib/testing";

export const LANGUAGE_MONACO_LANGUAGES: Record<Language, string> = {
export const MONACO_LANGUAGE_ID: Record<Language, string> = {
[Language.PHP]: "php",
[Language.TypeScript]: "typescript",
};
9 changes: 7 additions & 2 deletions src/components/editor-testing-panel.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,13 @@
cases: TestCasesStates<T, Inputs, Outputs>;
}
let { model, cases: initialData }: Props<Key, Inputs, Outputs> = $props();
let { model, cases }: Props<Key, Inputs, Outputs> = $props();
const states = $state(initialData);
let states = $state<TestCasesStates<Key, Inputs, Outputs>>([]);
$effect(() => {
states = cases;
});
let isRunning = $derived(states.some((c) => c.isRunning));
Expand All @@ -61,6 +65,7 @@

<button
class="btn btn-sm btn-primary"
class:hidden={states.length < 2}
onclick={async () => {
if (isRunning) {
return;
Expand Down
38 changes: 17 additions & 21 deletions src/components/editor.svelte
Original file line number Diff line number Diff line change
@@ -1,34 +1,30 @@
<script lang="ts" generics="R extends Runner">
<script lang="ts" generics="Lang extends Language">
import type { Snippet } from "svelte";
import { editor } from "monaco-editor";
import {
Language,
RUNNER_LANGUAGES,
RUNNER_TITLES,
Runner,
} from "@/lib/testing/runners";
import { LANGUAGE_MONACO_LANGUAGES } from "@/adapters/monaco";
Language,
LANGUAGE_TITLE,
} from "@/lib/testing/languages";
import { MONACO_LANGUAGE_ID } from "@/adapters/monaco";
interface Props {
initialValue: string;
runners: R[];
defaultRunner?: R;
children: Snippet<[R, editor.ITextModel]>;
onLanguageChange?: (lang: Language, model: editor.ITextModel) => void;
languages: Lang[];
initialValue?: string;
defaultLanguage?: Lang;
children: Snippet<[Lang, editor.ITextModel]>;
onLanguageChange?: (lang: Lang, model: editor.ITextModel) => void;
}
const { initialValue, runners, defaultRunner, children, onLanguageChange }: Props = $props();
const { initialValue = "", languages, defaultLanguage, children, onLanguageChange }: Props = $props();
let runner = $state(defaultRunner ?? runners[0]);
let lang = $derived(RUNNER_LANGUAGES[runner]);
let lang = $state(defaultLanguage ?? languages[0]);
$effect(() => {
onLanguageChange?.(lang, model);
});
let monacoLang = $derived(LANGUAGE_MONACO_LANGUAGES[lang]);
let monacoLang = $derived(MONACO_LANGUAGE_ID[lang]);
const model = editor.createModel(initialValue, $state.snapshot(monacoLang));
Expand All @@ -50,10 +46,10 @@

<div bind:this={editorElement} class="grow"></div>
<div class="p-4 border-t border-base-100 flex items-center gap-3">
{@render children(runner, model)}
<select class="select select-ghost select-sm ml-auto" bind:value={runner}>
{#each runners as runner (runner)}
<option value={runner}>{RUNNER_TITLES[runner]}</option>
{@render children(lang, model)}
<select class="select select-ghost select-sm ml-auto" bind:value={lang}>
{#each languages as lang (lang)}
<option value={lang}>{LANGUAGE_TITLE[lang]}</option>
{/each}
</select>
</div>
3 changes: 3 additions & 0 deletions src/containers/editor-provider.astro
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
<script>
import "monaco-editor";
import EditorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker";
import TsWorker from "monaco-editor/esm/vs/language/typescript/ts.worker?worker";

window.MonacoEnvironment = {
getWorker(_, label) {
switch (label) {
case "editorWorkerService":
return new EditorWorker();
case "typescript":
return new TsWorker();
default:
throw new Error(`Unknown label ${label}`);
}
Expand Down
44 changes: 29 additions & 15 deletions src/content/design-patterns/factory/editor.svelte
Original file line number Diff line number Diff line change
@@ -1,39 +1,53 @@
<script lang="ts">
import { Language, RUNNER_LANGUAGES, Runner } from '@/lib/testing';
import { Language } from '@/lib/testing';
import Editor from '@/components/editor.svelte';
import EditorTestingPanel, { type TestCasesStates } from '@/components/editor-testing-panel.svelte';
import { testCases, CaseType, type Inputs, type Outputs, CASE_TYPES } from './test-cases'
import { testRunnerFactories as phpTestRunnerFactories } from './php/test-runners'
import code from './php/code.php?raw';
const defaultRunner = Runner.PhpWasm
import { tsTestRunnerFactories, jsTestRunnerFactories } from './js/test-runners'
const INITIAL_VALUES: Record<Language, string> = {
[Language.PHP]: code,
const INITIAL_VALUES: Record<Language, Promise<string>> = {
[Language.PHP]: import('./php/code.php?raw').then(m => m.default),
[Language.TypeScript]: import('./js/code.ts?raw').then(m => m.default),
[Language.JavaScript]: import('./js/code.js?raw').then(m => m.default),
}
const CASES_FACTORIES: Record<Runner, () => TestCasesStates<CaseType, Inputs, Outputs>> = {
[Runner.PhpWasm]: () => CASE_TYPES.map(id => ({
const CASES_FACTORIES: Record<Language, () => TestCasesStates<CaseType, Inputs, Outputs>> = {
[Language.PHP]: () => CASE_TYPES.map(id => ({
id,
isRunning: false,
lastTestId: -1,
testCase: testCases[id],
testRunner: phpTestRunnerFactories[id],
})) as TestCasesStates<CaseType, Inputs, Outputs>,
[Language.TypeScript]: () => CASE_TYPES.map(id => ({
id,
isRunning: false,
lastTestId: -1,
testCase: testCases[id],
testRunner: tsTestRunnerFactories[id],
})) as TestCasesStates<CaseType, Inputs, Outputs>,
[Language.JavaScript]: () => CASE_TYPES.map(id => ({
id,
isRunning: false,
lastTestId: -1,
testCase: testCases[id],
testRunner: jsTestRunnerFactories[id],
})) as TestCasesStates<CaseType, Inputs, Outputs>,
}
const defaultLanguage = Language.PHP;
</script>

<Editor
{defaultRunner}
initialValue={INITIAL_VALUES[RUNNER_LANGUAGES[defaultRunner]]}
runners={[Runner.PhpWasm]}
onLanguageChange={(lang, model) => {
model.setValue(INITIAL_VALUES[lang])
{defaultLanguage}
languages={[Language.PHP, Language.TypeScript, Language.JavaScript]}
onLanguageChange={async (lang, model) => {
model.setValue(await INITIAL_VALUES[lang])
}}
>
{#snippet children(runner, model)}
<EditorTestingPanel {model} cases={CASES_FACTORIES[runner]()} />
{#snippet children(lang, model)}
<EditorTestingPanel {model} cases={CASES_FACTORIES[lang]()} />
{/snippet}
</Editor>
25 changes: 25 additions & 0 deletions src/content/design-patterns/factory/js/code.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const PaymentSystemType = {
PayPal: "paypal",
WebMoney: "webmoney",
CatBank: "cat-bank",
};

export function case1(type, base, amount) {
throw new Error("Not implemented");
}








const PaymentSystemActionType = {
Payment: "payment",
Payout: "payout",
};

export function case2(type, base, action, amount) {
throw new Error("Not implemented");
}
36 changes: 36 additions & 0 deletions src/content/design-patterns/factory/js/code.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const enum PaymentSystemType {
PayPal = "paypal",
WebMoney = "webmoney",
CatBank = "cat-bank",
}

export function case1(
type: PaymentSystemType,
base: number,
amount: number
): number {
throw new Error("Not implemented");
}










const enum PaymentSystemActionType {
Payment = "payment",
Payout = "payout",
}

export function case2(
type: PaymentSystemType,
base: number,
action: PaymentSystemActionType,
amount: number
): number {
throw new Error("Not implemented");
}
11 changes: 11 additions & 0 deletions src/content/design-patterns/factory/js/model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { PaymentSystemActionType, PaymentSystemType } from "../reference";

export interface TestingModule {
case1(type: PaymentSystemType, base: number, amount: number): number;
case2(
type: PaymentSystemType,
base: number,
action: PaymentSystemActionType,
amount: number
): number;
}
71 changes: 71 additions & 0 deletions src/content/design-patterns/factory/js/test-runners.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import type { TestRunnerFactory } from "@/lib/testing";
import { TsTestRunner, JsTestRunner } from "@/lib/testing/js";

import {
CaseType,
type ComplexTestInput,
type Inputs,
type Outputs,
type SimpleTestInput,
} from "../test-cases";
import type { TestingModule } from "./model";

class SimpleJsTestRunner extends JsTestRunner<
TestingModule,
SimpleTestInput,
number
> {
async executeTest(m: TestingModule, input: SimpleTestInput): Promise<number> {
return m.case1(input.paymentSystem, input.base, input.amount);
}
}

class ComplexJsTestRunner extends JsTestRunner<
TestingModule,
ComplexTestInput,
number
> {
async executeTest(
m: TestingModule,
input: ComplexTestInput
): Promise<number> {
return m.case2(input.paymentSystem, input.base, input.action, input.amount);
}
}

export const jsTestRunnerFactories: {
[k in CaseType]: TestRunnerFactory<Inputs[k], Outputs[k]>;
} = {
[CaseType.Simple]: async (code) => new SimpleJsTestRunner(code),
[CaseType.Complex]: async (code) => new ComplexJsTestRunner(code),
};

class SimpleTsTestRunner extends TsTestRunner<
TestingModule,
SimpleTestInput,
number
> {
async executeTest(m: TestingModule, input: SimpleTestInput): Promise<number> {
return m.case1(input.paymentSystem, input.base, input.amount);
}
}

class ComplexTsTestRunner extends TsTestRunner<
TestingModule,
ComplexTestInput,
number
> {
async executeTest(
m: TestingModule,
input: ComplexTestInput
): Promise<number> {
return m.case2(input.paymentSystem, input.base, input.action, input.amount);
}
}

export const tsTestRunnerFactories: {
[k in CaseType]: TestRunnerFactory<Inputs[k], Outputs[k]>;
} = {
[CaseType.Simple]: async (code) => new SimpleTsTestRunner(code),
[CaseType.Complex]: async (code) => new ComplexTsTestRunner(code),
};
25 changes: 0 additions & 25 deletions src/content/design-patterns/factory/php/complex-test-runner.ts

This file was deleted.

21 changes: 0 additions & 21 deletions src/content/design-patterns/factory/php/simple-test-runner.ts

This file was deleted.

Loading

0 comments on commit 1e6a3df

Please sign in to comment.