22import { Heading } from "@/[docs_id]/markdown" ;
33import "mocha/mocha.js" ;
44import "mocha/mocha.css" ;
5- import { useEffect , useRef , useState } from "react" ;
6- import { usePyodide } from "./python/runtime" ;
5+ import { Fragment , useEffect , useRef , useState } from "react" ;
76import { useWandbox } from "./wandbox/runtime" ;
87import { RuntimeContext , RuntimeLang } from "./runtime" ;
98import { useEmbedContext } from "./embedContext" ;
109import { defineTests } from "./tests" ;
10+ import { usePyodide } from "./worker/pyodide" ;
11+ import { useRuby } from "./worker/ruby" ;
12+ import { useJSEval } from "./worker/jsEval" ;
13+ import { ReplTerminal } from "./repl" ;
14+ import { EditorComponent , getAceLang } from "./editor" ;
15+ import { ExecFile } from "./exec" ;
1116
1217export default function RuntimeTestPage ( ) {
18+ return (
19+ < div className = "p-4 mx-auto w-full max-w-200" >
20+ < Heading level = { 1 } > Runtime Test Page</ Heading >
21+
22+ < Heading level = { 2 } > REPLとコード実行のサンプル</ Heading >
23+ { /* name of each tab group should be unique */ }
24+ < div className = "tabs tabs-border" >
25+ { Object . entries ( sampleConfig ) . map ( ( [ lang , config ] ) => (
26+ < Fragment key = { lang } >
27+ < input
28+ type = "radio"
29+ name = "runtime-sample-tabs"
30+ className = "tab"
31+ aria-label = { lang }
32+ />
33+ < div className = "tab-content border-base-300 bg-base-100 p-4" >
34+ < RuntimeSample lang = { lang as RuntimeLang } config = { config } />
35+ </ div >
36+ </ Fragment >
37+ ) ) }
38+ </ div >
39+
40+ < Heading level = { 2 } > 自動テスト</ Heading >
41+ < MochaTest />
42+ </ div >
43+ ) ;
44+ }
45+
46+ interface SampleConfig {
47+ repl : boolean ;
48+ replInitContent ?: string ; // ReplOutput[] ではない。stringのパースはruntimeが行う
49+ editor : Record < string , string > | false ;
50+ exec : string [ ] | false ;
51+ }
52+ const sampleConfig : Record < RuntimeLang , SampleConfig > = {
53+ python : {
54+ repl : true ,
55+ replInitContent : '>>> print("Hello, World!")\nHello, World!' ,
56+ editor : {
57+ "main.py" : 'print("Hello, World!")' ,
58+ } ,
59+ exec : [ "main.py" ] ,
60+ } ,
61+ ruby : {
62+ repl : true ,
63+ replInitContent : '>> puts "Hello, World!"\nHello, World!' ,
64+ editor : {
65+ "main.rb" : 'puts "Hello, World!"' ,
66+ } ,
67+ exec : [ "main.rb" ] ,
68+ } ,
69+ javascript : {
70+ repl : true ,
71+ replInitContent : '> console.log("Hello, World!");\nHello, World!' ,
72+ editor : false ,
73+ exec : false ,
74+ } ,
75+ cpp : {
76+ repl : false ,
77+ editor : {
78+ "main.cpp" : `#include <iostream>
79+ #include "sub.h"
80+
81+ int main() {
82+ std::cout << "Hello, World!" << std::endl;
83+ }` ,
84+ "sub.h" : `` ,
85+ "sub.cpp" : `` ,
86+ } ,
87+ exec : [ "main.cpp" , "sub.cpp" ] ,
88+ } ,
89+ } ;
90+ function RuntimeSample ( {
91+ lang,
92+ config,
93+ } : {
94+ lang : RuntimeLang ;
95+ config : SampleConfig ;
96+ } ) {
97+ return (
98+ < div className = "flex flex-col gap-4" >
99+ { config . repl && (
100+ < ReplTerminal
101+ terminalId = "1"
102+ language = { lang }
103+ initContent = { config . replInitContent }
104+ />
105+ ) }
106+ { config . editor &&
107+ Object . entries ( config . editor ) . map ( ( [ filename , initContent ] ) => (
108+ < EditorComponent
109+ key = { filename }
110+ language = { getAceLang ( lang ) }
111+ filename = { filename }
112+ initContent = { initContent }
113+ />
114+ ) ) }
115+ { config . exec && (
116+ < ExecFile filenames = { config . exec } language = { lang } content = "" />
117+ ) }
118+ </ div >
119+ ) ;
120+ }
121+
122+ function MochaTest ( ) {
13123 const pyodide = usePyodide ( ) ;
124+ const ruby = useRuby ( ) ;
125+ const javascript = useJSEval ( ) ;
14126 const wandboxCpp = useWandbox ( "cpp" ) ;
15127 const runtimeRef = useRef < Record < RuntimeLang , RuntimeContext > > ( null ! ) ;
16128 runtimeRef . current = {
17129 python : pyodide ,
130+ ruby : ruby ,
131+ javascript : javascript ,
18132 cpp : wandboxCpp ,
19133 } ;
20- const { files, writeFile } = useEmbedContext ( ) ;
21- const filesRef = useRef < Record < string , string > > ( { } ) ;
22- filesRef . current = files ;
23- const [ mochaState , setMochaState ] = useState < "idle" | "running" | "finished" > (
24- "idle"
25- ) ;
134+
26135 const [ searchParams , setSearchParams ] = useState < string > ( "" ) ;
27136 useEffect ( ( ) => {
28137 setSearchParams ( window . location . search ) ;
29138 } , [ ] ) ;
139+ const [ mochaState , setMochaState ] = useState < "idle" | "running" | "finished" > (
140+ "idle"
141+ ) ;
142+ const { writeFile } = useEmbedContext ( ) ;
30143
31144 const runTest = ( ) => {
32145 setMochaState ( "running" ) ;
@@ -44,59 +157,56 @@ export default function RuntimeTestPage() {
44157 } ;
45158
46159 return (
47- < div className = "p-4 mx-auto w-full max-w-200" >
48- < Heading level = { 1 } > Runtime Test Page</ Heading >
49- < div className = "border-1 border-transparent translate-x-0" >
50- { /* margin collapseさせない & fixedの対象をviewportではなくこのdivにする */ }
51- { mochaState === "idle" ? (
52- < button className = "btn btn-primary mt-4" onClick = { runTest } >
53- テストを実行
54- </ button >
55- ) : mochaState === "running" ? (
56- < div className = "alert mt-16 sm:mt-4 w-80" >
57- < svg
58- xmlns = "http://www.w3.org/2000/svg"
59- className = "animate-spin h-5 w-5 mr-3 border-2 border-solid border-current border-t-transparent rounded-full"
60- fill = "none"
61- viewBox = "0 0 24 24"
62- > </ svg >
63- テストを実行中です...
64- </ div >
65- ) : (
66- < div className = "alert mt-16 sm:mt-4 w-80" >
67- < svg
68- xmlns = "http://www.w3.org/2000/svg"
69- fill = "none"
70- viewBox = "0 0 24 24"
71- className = "stroke-info h-6 w-6 shrink-0"
72- >
73- < path
74- strokeLinecap = "round"
75- strokeLinejoin = "round"
76- strokeWidth = "2"
77- d = "M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
78- > </ path >
79- </ svg >
80- テストが完了しました
81- </ div >
160+ < div className = "border-1 border-transparent translate-x-0" >
161+ { /* margin collapseさせない & fixedの対象をviewportではなくこのdivにする */ }
162+ { mochaState === "idle" ? (
163+ < button className = "btn btn-primary mt-4" onClick = { runTest } >
164+ テストを実行
165+ </ button >
166+ ) : mochaState === "running" ? (
167+ < div className = "alert mt-16 sm:mt-4 w-80" >
168+ < svg
169+ xmlns = "http://www.w3.org/2000/svg"
170+ className = "animate-spin h-5 w-5 mr-3 border-2 border-solid border-current border-t-transparent rounded-full"
171+ fill = "none"
172+ viewBox = "0 0 24 24"
173+ > </ svg >
174+ テストを実行中です...
175+ </ div >
176+ ) : (
177+ < div className = "alert mt-16 sm:mt-4 w-80" >
178+ < svg
179+ xmlns = "http://www.w3.org/2000/svg"
180+ fill = "none"
181+ viewBox = "0 0 24 24"
182+ className = "stroke-info h-6 w-6 shrink-0"
183+ >
184+ < path
185+ strokeLinecap = "round"
186+ strokeLinejoin = "round"
187+ strokeWidth = "2"
188+ d = "M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
189+ > </ path >
190+ </ svg >
191+ テストが完了しました
192+ </ div >
193+ ) }
194+ < p className = "mt-8" >
195+ { new URLSearchParams ( searchParams ) . has ( "grep" ) && (
196+ < >
197+ 一部のテストだけを実行します:
198+ < code className = "ml-2 font-mono" >
199+ { new URLSearchParams ( searchParams ) . get ( "grep" ) }
200+ </ code >
201+ { /* eslint-disable-next-line @next/next/no-html-link-for-pages */ }
202+ < a className = "ml-4 link link-info" href = "/terminal" >
203+ { /* aタグでページをリロードしないと動作しない。 */ }
204+ フィルタを解除
205+ </ a >
206+ </ >
82207 ) }
83- < p className = "mt-8" >
84- { new URLSearchParams ( searchParams ) . has ( "grep" ) && (
85- < >
86- 一部のテストだけを実行します:
87- < code className = "ml-2 font-mono" >
88- { new URLSearchParams ( searchParams ) . get ( "grep" ) }
89- </ code >
90- { /* eslint-disable-next-line @next/next/no-html-link-for-pages */ }
91- < a className = "ml-4 link link-info" href = "/terminal" >
92- { /* aタグでページをリロードしないと動作しない。 */ }
93- フィルタを解除
94- </ a >
95- </ >
96- ) }
97- </ p >
98- < div className = "m-0!" id = "mocha" />
99- </ div >
208+ </ p >
209+ < div className = "m-0!" id = "mocha" />
100210 </ div >
101211 ) ;
102212}
0 commit comments