impl preprocessor
This commit is contained in:
parent
c596a80518
commit
ca9658be6b
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,5 +2,3 @@ book
|
||||
.env
|
||||
cache
|
||||
.DS_Store
|
||||
build
|
||||
|
||||
|
@ -2,13 +2,15 @@
|
||||
authors = ["monoid"]
|
||||
language = "ko"
|
||||
multilingual = false
|
||||
src = "build"
|
||||
src = "src"
|
||||
title = "Software Requirement Specification"
|
||||
|
||||
[preprocessor]
|
||||
|
||||
[preprocessor.mermaid]
|
||||
command = "mdbook-mermaid"
|
||||
[preprocessor.etap]
|
||||
command = "deno run -A --no-check tools/preprop.ts"
|
||||
before = ["mermaid"]
|
||||
|
||||
[output]
|
||||
|
||||
|
21
cli.py
21
cli.py
@ -18,15 +18,6 @@ def updateIssue(issuePath: str, verbose = False):
|
||||
cmd = ["deno", "run","--no-check", "-A","tools/getIssue.ts", "--path",issuePath]
|
||||
cmdExecute(cmd, verbose, "update issue:")
|
||||
|
||||
def printDocument(issuePath:str, outDir:str, watch = False, verbose = False):
|
||||
if verbose:
|
||||
print("build document : issuePath(", issuePath, ") to ", outDir)
|
||||
cmd = ["deno", "run","--no-check" ,"-A","tools/printDocument.ts", "--issue_path", issuePath, "--outDir", outDir]
|
||||
if watch:
|
||||
cmd.append("--watch")
|
||||
p = subprocess.run(cmd)
|
||||
p.check_returncode()
|
||||
|
||||
def build(args):
|
||||
parser = argparse.ArgumentParser(description='Compiling the documentation', prog="cli.py build")
|
||||
parser.add_argument('-v', '--verbose', action='store_true', help='verbose mode')
|
||||
@ -38,10 +29,10 @@ def build(args):
|
||||
if args.verbose:
|
||||
print("build start")
|
||||
|
||||
issuePath = os.path.join(args.outDir,"issues.json")
|
||||
if args.update_issues:
|
||||
os.makedirs("cache", exist_ok=True)
|
||||
issuePath = os.path.join("cache","issues.json")
|
||||
updateIssue(issuePath, args.verbose)
|
||||
printDocument(issuePath, args.outDir, args.watch, args.verbose)
|
||||
cmd = ["mdbook", "build"]
|
||||
cmdExecute(cmd, args.verbose)
|
||||
|
||||
@ -57,11 +48,7 @@ def serve(args):
|
||||
if args.verbose:
|
||||
print("serve start")
|
||||
cmd = ["mdbook", "serve", "--port", str(args.port)]
|
||||
p = subprocess.Popen(cmd)
|
||||
printDocument(issuePath, outDir, True, args.verbose)
|
||||
p.wait()
|
||||
if p.returncode != 0:
|
||||
sys.exit(p.returncode)
|
||||
cmdExecute(cmd, args.verbose, "serve:")
|
||||
|
||||
def help(_args):
|
||||
global commandList
|
||||
@ -80,7 +67,7 @@ def issueUpdate(args):
|
||||
def buildPdf(args):
|
||||
parser = argparse.ArgumentParser(description='Print to pdf', prog="cli.py buildPdf")
|
||||
parser.add_argument('-v', '--verbose', action='store_true', help='verbose mode')
|
||||
parser.add_argument('--outDir', default="build/doc.pdf", help='output directory')
|
||||
parser.add_argument('--outDir', default="cache/doc.pdf", help='output directory')
|
||||
parser.add_argument('--browser-path', help='path to the browser')
|
||||
args = parser.parse_args(args)
|
||||
if os.path.exists(args.outDir):
|
||||
|
@ -4,4 +4,5 @@
|
||||
- [Overall Description](./overall.md)
|
||||
- [Specific Requirement](./specific.md)
|
||||
- [Supporting information](./support.md)
|
||||
- [Architecture](./architecture.md)
|
||||
- [Architecture](./architecture.md)
|
||||
- [Testing](./testing.md)
|
335
src/testing.md
Normal file
335
src/testing.md
Normal file
@ -0,0 +1,335 @@
|
||||
# Testing
|
||||
|
||||
## 유닛 테스트
|
||||
|
||||
유닛 테스트로 69.6%의 Line Coverage와 73.4%의 Function Coverage를 달성했다.
|
||||
다음과 같은 로그가 있다.
|
||||
|
||||
```
|
||||
running 2 tests from ./src/auth/permission.test.ts
|
||||
permission.test ... ok (8ms)
|
||||
permission empty ... ok (16ms)
|
||||
running 4 tests from ./src/auth/session.test.ts
|
||||
Session ...
|
||||
set ... ok (9ms)
|
||||
delete ... ok (16ms)
|
||||
ok (42ms)
|
||||
Login Handler ...
|
||||
login with invalid format ... ok (15ms)
|
||||
login with invalid password ... ok (16ms)
|
||||
login ... ok (16ms)
|
||||
logout with no session ... ok (16ms)
|
||||
logout ... ok (16ms)
|
||||
ok (96ms)
|
||||
getSession ... ok (16ms)
|
||||
getSession with invalid cookie ... ok (16ms)
|
||||
running 1 test from ./src/auth/user.test.ts
|
||||
user.createAdminUser ... ok (15ms)
|
||||
running 4 tests from ./src/document/filedoc.test.ts
|
||||
readDocFile ... ok (19ms)
|
||||
readDocFile: not found ... ok (16ms)
|
||||
readDocFile: invalid json ... ok (16ms)
|
||||
saveDocFile ... ok (15ms)
|
||||
running 3 tests from ./src/router/methodHandle.test.ts
|
||||
methodHandle: basic methods ... ok (8ms)
|
||||
methodHandle: not found ... ok (16ms)
|
||||
methodHandle: options ... ok (16ms)
|
||||
running 8 tests from ./src/router/route.test.ts
|
||||
route: basic route ... ok (10ms)
|
||||
route: double slash route ... ok (16ms)
|
||||
route: double match ... ok (16ms)
|
||||
route: test context ... ok (16ms)
|
||||
route: test regex ... ok (16ms)
|
||||
route: test not found ... ok (16ms)
|
||||
route: encode_route ... ok (2ms)
|
||||
route: router in router ... ok (13ms)
|
||||
running 4 tests from ./src/rpc/chunk.test.ts
|
||||
basic chunk operation ...
|
||||
create chunk ... ok (19ms)
|
||||
delete chunk ... ok (15ms)
|
||||
modify chunk ... ok (15ms)
|
||||
move chunk ... ok (15ms)
|
||||
invalid chunk operation ... ok (17ms)
|
||||
ok (98ms)
|
||||
test chunk notification operation ... ok (15ms)
|
||||
test chunk conflict ... ok (16ms)
|
||||
test chunk conflict resolve with history ... ok (32ms)
|
||||
running 2 tests from ./src/rpc/doc.test.ts
|
||||
handleDocumentMethod ... ok (4ms)
|
||||
handleTagMethod ...
|
||||
setTag ... ok (13ms)
|
||||
getTag ... ok (15ms)
|
||||
conflict ... ok (15ms)
|
||||
ok (61ms)
|
||||
running 3 tests from ./src/rpc/share.test.ts
|
||||
handleShareGetInfo ... ok (18ms)
|
||||
handleShareDocMethod ... ok (15ms)
|
||||
handleShareMethod with no existing share token ... ok (16ms)
|
||||
running 1 test from ./src/server.test.ts
|
||||
server rpc test ... ok (1s)
|
||||
running 3 tests from ./src/setting.test.ts
|
||||
setting: basic ... ok (35ms)
|
||||
setting: default value ... ok (7ms)
|
||||
setting: defered register ... ok (16ms)
|
||||
test result: ok. 35 passed (15 steps); 0 failed; 0 ignored; 0 measured; 0 filtered out (2s)
|
||||
```
|
||||
|
||||
|
||||
## 기능 테스트
|
||||
|
||||
### Chunk
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Content</th>
|
||||
<th>Procedure</th>
|
||||
<th>Test Data</th>
|
||||
<th>P/F</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>Focus/Unfocus</td>
|
||||
<td>1. 청크를 클릭한다.</td>
|
||||
<td></td>
|
||||
<td>P</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>remove</td>
|
||||
<td>1. 청크를 삭제하는 버튼을 클릭한다.</td>
|
||||
<td></td>
|
||||
<td>P</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3-1</td>
|
||||
<td>render - markdown</td>
|
||||
<td>1. 마크다운 청크 렌더링을 확인한다.</td>
|
||||
<td> # 제목 </td>
|
||||
<td>P</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3-2</td>
|
||||
<td>render - latex</td>
|
||||
<td>1. LaTex 청크 렌더링을 확인한다.</td>
|
||||
<td> sum^n_{n=0}n = \frac{n(n+1)}2$$ </td>
|
||||
<td>P</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>3-3</td>
|
||||
<td>render - link</td>
|
||||
<td>1. Image 청크 렌더링을 확인한다.</td>
|
||||
<td>http://picsum.photos</td>
|
||||
<td>P</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>4</td>
|
||||
<td>previews</td>
|
||||
<td>1. Katex 청크의 미리보기를 본다.</td>
|
||||
<td> sum^n_{n=0}n = \frac{n(n+1)}2$$ </td>
|
||||
<td>F</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>10</td>
|
||||
<td>autocomplete</td>
|
||||
<td>1. <kbd>Ctrl+Space</kbd>를 눌러 자동완성을 시도한다.</td>
|
||||
<td></td>
|
||||
<td>F</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>11</td>
|
||||
<td>swap positions</td>
|
||||
<td>1. 청크의 위치를 바꾼다.</td>
|
||||
<td></td>
|
||||
<td>P</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>27-1</td>
|
||||
<td>edit</td>
|
||||
<td>1. 청크를 수정한다.</td>
|
||||
<td></td>
|
||||
<td>P</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>27-2</td>
|
||||
<td>edit chunk conflict</td>
|
||||
<td>1. 청크를 수정모드에 들어간다.</td>
|
||||
<td></td>
|
||||
<td>F</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
### Document
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Content</th>
|
||||
<th>Procedure</th>
|
||||
<th>Test Data</th>
|
||||
<th>P/F</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>5</td>
|
||||
<td>view Chunk</td>
|
||||
<td>1. 문서를 열어 청크가 렌더링되는지 본다.</td>
|
||||
<td>test.syd</td>
|
||||
<td>P</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>7</td>
|
||||
<td>add/delete tag</td>
|
||||
<td>1. 문서에 태그를 추가한다.<br>2. 문서에 태그를 삭제한다.</td>
|
||||
<td>A</td>
|
||||
<td>P</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>8</td>
|
||||
<td>Drag And Drop Upload,</td>
|
||||
<td>1. 텍스트를 드래그한다.</td>
|
||||
<td></td>
|
||||
<td>P</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
### File
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Content</th>
|
||||
<th>Procedure</th>
|
||||
<th>Test Data</th>
|
||||
<th>P/F</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>14</td>
|
||||
<td>create/delete/rename file</td>
|
||||
<td>1. 파일을 만든다.</td>
|
||||
<td>test.txt</td>
|
||||
<td>P</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>15</td>
|
||||
<td>upload/download files</td>
|
||||
<td>1. 파일을 업로드한다.</td>
|
||||
<td>test.txt</td>
|
||||
<td>P</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>18</td>
|
||||
<td>export document</td>
|
||||
<td>1. export 버튼을 누른다.</td>
|
||||
<td></td>
|
||||
<td>F</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
### Search
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Content</th>
|
||||
<th>Procedure</th>
|
||||
<th>Test Data</th>
|
||||
<th>P/F</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>16</td>
|
||||
<td>Document Search</td>
|
||||
<td>1. 검색버튼을 눌러 검색을 한다.</td>
|
||||
<td>chunk</td>
|
||||
<td>F</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
### Stash
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Content</th>
|
||||
<th>Procedure</th>
|
||||
<th>Test Data</th>
|
||||
<th>P/F</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>17</td>
|
||||
<td>render</td>
|
||||
<td>1. 스태시가 그려지는지 확인한다</td>
|
||||
<td></td>
|
||||
<td>P</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>19</td>
|
||||
<td>add</td>
|
||||
<td>1. 청크를 추가한다</td>
|
||||
<td></td>
|
||||
<td>P</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>20</td>
|
||||
<td>remove</td>
|
||||
<td>1. 청크를 삭제한다</td>
|
||||
<td></td>
|
||||
<td>P</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>21</td>
|
||||
<td>Drag and Drop to Document</td>
|
||||
<td>1. 청크로부터 문서로 청크를 옮긴다.</td>
|
||||
<td></td>
|
||||
<td>P</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
### Management
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Content</th>
|
||||
<th>Procedure</th>
|
||||
<th>Test Data</th>
|
||||
<th>P/F</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>22</td>
|
||||
<td>Login</td>
|
||||
<td>1. 비밀번호를 입력한다.</td>
|
||||
<td>admin</td>
|
||||
<td>F</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>24</td>
|
||||
<td>Localization</td>
|
||||
<td>1. 다른언어를 지원하는지 언어를 바꿔 확인한다</td>
|
||||
<td></td>
|
||||
<td>F</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
86
tools/preprop.ts
Normal file
86
tools/preprop.ts
Normal file
@ -0,0 +1,86 @@
|
||||
import { readAll } from "https://deno.land/std@0.143.0/streams/mod.ts";
|
||||
import * as log from "https://deno.land/std@0.143.0/log/mod.ts";
|
||||
import { WriterHandler } from "https://deno.land/std@0.143.0/log/handlers.ts";
|
||||
import * as Eta from "https://deno.land/x/eta@v1.12.3/mod.ts";
|
||||
import { Issue } from "./githubType.ts";
|
||||
|
||||
class StderrHandler extends WriterHandler {
|
||||
protected _writer: Deno.Writer;
|
||||
#encoder: TextEncoder;
|
||||
constructor(levelName: log.LevelName, options: log.HandlerOptions = {}){
|
||||
super(levelName, options);
|
||||
this.#encoder = new TextEncoder();
|
||||
this._writer = Deno.stderr;
|
||||
}
|
||||
log(msg: string): void {
|
||||
const encoded = this.#encoder.encode(msg);
|
||||
Deno.stderr.writeSync(encoded);
|
||||
}
|
||||
}
|
||||
|
||||
interface Book {
|
||||
sections: Section[];
|
||||
}
|
||||
interface Section {
|
||||
//chapter or separtor or PartTitle
|
||||
Chapter: Chapter;
|
||||
}
|
||||
interface Chapter {
|
||||
name: string;
|
||||
content: string;
|
||||
/** section number */
|
||||
number?: number[];
|
||||
sub_items: Section[];
|
||||
path?: string;
|
||||
source_path?: string;
|
||||
parent_names: string[];
|
||||
}
|
||||
|
||||
if (import.meta.main) {
|
||||
await log.setup({
|
||||
handlers: {
|
||||
console: new StderrHandler("INFO", {
|
||||
formatter: "{levelName} {msg}",
|
||||
}),
|
||||
},
|
||||
loggers: {
|
||||
default: {
|
||||
level: "INFO",
|
||||
handlers: ["console"],
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
main(Deno.args);
|
||||
}
|
||||
|
||||
async function getIssues(){
|
||||
const issue_path = "cache/issues.json";
|
||||
const c = await Deno.readTextFile(issue_path);
|
||||
const issues = JSON.parse(c) as Issue[];
|
||||
issues.sort((a, b) => a.number - b.number);
|
||||
return issues;
|
||||
}
|
||||
|
||||
async function main(args: string[]) {
|
||||
if (args.length > 1) {
|
||||
//log.info(`args: ${JSON.stringify(args)}`);
|
||||
if (args[0] === "supports") {
|
||||
Deno.exit(0);
|
||||
}
|
||||
}
|
||||
const issues = await getIssues();
|
||||
|
||||
log.info(`start`);
|
||||
const data = await readAll(Deno.stdin);
|
||||
const jsonText = new TextDecoder().decode(data);
|
||||
await Deno.writeTextFile("log.json", jsonText);
|
||||
const [context, book] = JSON.parse(jsonText) as [any, Book];
|
||||
book.sections.forEach(x=>{
|
||||
x.Chapter.content = Eta.render(x.Chapter.content, {
|
||||
issues: issues
|
||||
}) as string;
|
||||
})
|
||||
//Deno.stderr.writeSync(new TextEncoder().encode(`context: ${JSON.stringify(context)}\n`));
|
||||
console.log(JSON.stringify(book));
|
||||
}
|
@ -1,137 +0,0 @@
|
||||
|
||||
import { Issue } from "./githubType.ts";
|
||||
import { copy } from "https://deno.land/std@0.136.0/fs/mod.ts";
|
||||
import { readAll } from "https://deno.land/std@0.135.0/streams/mod.ts"
|
||||
import { parse as argParse } from "https://deno.land/std@0.135.0/flags/mod.ts";
|
||||
import {
|
||||
normalize, join as pathJoin, fromFileUrl, parse as parsePath
|
||||
, relative
|
||||
} from "https://deno.land/std@0.135.0/path/mod.ts";
|
||||
import * as Eta from "https://deno.land/x/eta@v1.12.3/mod.ts";
|
||||
import { createReactive } from "./reactivity.ts";
|
||||
|
||||
async function readContent(path?: string): Promise<string> {
|
||||
let content = "[]";
|
||||
if (path) {
|
||||
content = await Deno.readTextFile(path);
|
||||
}
|
||||
else if (!Deno.isatty(Deno.stdin.rid)) {
|
||||
const decoder = new TextDecoder(undefined, { ignoreBOM: true });
|
||||
const buf = await readAll(Deno.stdin);
|
||||
content = decoder.decode(buf);
|
||||
}
|
||||
else throw new Error("No input provided. path or stdin.");
|
||||
return content;
|
||||
}
|
||||
type printDocParam = {
|
||||
target: string,
|
||||
data: {
|
||||
issues: Issue[]
|
||||
}
|
||||
};
|
||||
|
||||
async function printDoc(param: printDocParam, option?: {
|
||||
outDir?: string
|
||||
}) {
|
||||
option = option ?? {};
|
||||
const { target, data } = param;
|
||||
const { outDir } = option;
|
||||
|
||||
let print: string = "";
|
||||
print = await Eta.renderFile(target, data) as string;
|
||||
if (outDir) {
|
||||
const outPath = pathJoin(outDir, target);
|
||||
await Deno.mkdir(pathJoin(outDir), { recursive: true });
|
||||
await Deno.writeTextFile(outPath, print);
|
||||
}
|
||||
else {
|
||||
console.log(print);
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const parsedArg = argParse(Deno.args);
|
||||
const { issue_path, outDirArg, w, watch } = parsedArg;
|
||||
const watchMode = w || watch;
|
||||
if (typeof issue_path !== "undefined" && typeof issue_path !== "string") {
|
||||
console.log("Please provide a path to the json file.");
|
||||
Deno.exit(1);
|
||||
}
|
||||
if (typeof outDirArg !== "undefined" && typeof outDirArg !== "string") {
|
||||
console.log("Please provide a path to the output file.");
|
||||
Deno.exit(1);
|
||||
}
|
||||
const outDir = (outDirArg ?? "build");
|
||||
if (typeof watchMode !== "undefined" && typeof watchMode !== "boolean") {
|
||||
console.log("Please provide a boolean value for w.");
|
||||
Deno.exit(1);
|
||||
}
|
||||
if (watchMode && typeof issue_path === "undefined") {
|
||||
console.log("Could not set watch mode without a path.");
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
const url = new URL(import.meta.url)
|
||||
url.pathname = normalize(pathJoin(url.pathname, "..", "template"));
|
||||
const viewPath = fromFileUrl(url);
|
||||
Eta.configure({
|
||||
views: viewPath,
|
||||
"view cache": false,
|
||||
});
|
||||
const issuesR = await createReactive(async () => {
|
||||
const c = await readContent(issue_path);
|
||||
const issues = JSON.parse(c) as Issue[];
|
||||
issues.sort((a, b) => a.number - b.number);
|
||||
return issues;
|
||||
});
|
||||
|
||||
const targets = ["SUMMARY.md", "overall.md", "specific.md", "intro.md", "support.md", "architecture.md"];
|
||||
|
||||
const targetsR = await Promise.all(targets.map(async (t) => {
|
||||
return await createReactive(async () => {
|
||||
await printDoc({
|
||||
target: t, data: {
|
||||
issues: issuesR.value
|
||||
}
|
||||
}, { outDir: outDir });
|
||||
});
|
||||
}
|
||||
|
||||
));
|
||||
issuesR.wireTo(...targetsR);
|
||||
const copyOp = await createReactive(async () => {
|
||||
const files = [...Deno.readDirSync(viewPath)].map(x => x.name).filter(x => !x.endsWith(".md"));
|
||||
const op = files.map(x => copy(pathJoin(viewPath, x), pathJoin(outDir, x), { overwrite: true }));
|
||||
await Promise.all(op);
|
||||
});
|
||||
|
||||
if (watchMode) {
|
||||
const watcher = Deno.watchFs([viewPath, issue_path as string]);
|
||||
for await (const event of watcher) {
|
||||
if (event.kind === "modify") {
|
||||
Deno.stdout.write(
|
||||
new TextEncoder().encode("\x1b[2J\x1b[0f"),
|
||||
);
|
||||
console.log(`reloading ${event.paths.join(", ")}`);
|
||||
for (const path of event.paths) {
|
||||
const p = parsePath(path);
|
||||
if (p.dir === viewPath) {
|
||||
if (p.ext === ".md") {
|
||||
targetsR[targets.indexOf(p.base)].update();
|
||||
}
|
||||
else {
|
||||
copyOp.update();
|
||||
}
|
||||
}
|
||||
else if (p.base === "issues.json") {
|
||||
await issuesR.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (import.meta.main) {
|
||||
main();
|
||||
}
|
318
tools/printIssue.ts
Normal file
318
tools/printIssue.ts
Normal file
@ -0,0 +1,318 @@
|
||||
type Testcase = {
|
||||
id: number,
|
||||
subId: number | null,
|
||||
content: string,
|
||||
procedure: string,
|
||||
testData: string| null,
|
||||
expected: string,
|
||||
actual: string,
|
||||
pass: boolean
|
||||
}
|
||||
|
||||
const testcase: Testcase[] = [
|
||||
{
|
||||
"id": 1,
|
||||
"subId": null,
|
||||
"content": "Focus/Unfocus",
|
||||
"procedure": "1. 청크를 클릭한다.",
|
||||
"testData": null,
|
||||
"expected": "",
|
||||
"actual": "",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"subId": null,
|
||||
"content": "remove",
|
||||
"procedure": "1. 청크를 삭제하는 버튼을 클릭한다.",
|
||||
"testData": null,
|
||||
"expected": "",
|
||||
"actual": "",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"subId": 1,
|
||||
"content": "render - markdown",
|
||||
"procedure": "1. 마크다운 청크 렌더링을 확인한다.",
|
||||
"testData": " # 제목 ",
|
||||
"expected": "",
|
||||
"actual": "",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"subId": 2,
|
||||
"content": "render - latex",
|
||||
"procedure": "1. LaTex 청크 렌더링을 확인한다.",
|
||||
"testData": " sum^n_{n=0}n = \\frac{n(n+1)}2$$ ",
|
||||
"expected": "",
|
||||
"actual": "",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"subId": 3,
|
||||
"content": "render - link",
|
||||
"procedure": "1. Image 청크 렌더링을 확인한다.",
|
||||
"testData": " http://picsum.photos/200/300 ",
|
||||
"expected": "",
|
||||
"actual": "",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"subId": null,
|
||||
"content": "previews",
|
||||
"procedure": "1. Katex 청크의 미리보기를 본다.",
|
||||
"testData": " sum^n_{n=0}n = \\frac{n(n+1)}2$$ ",
|
||||
"expected": "",
|
||||
"actual": "",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"subId": null,
|
||||
"content": "autocomplete",
|
||||
"procedure": "1. 자동완성을 시험한다.",
|
||||
"testData": null,
|
||||
"expected": "",
|
||||
"actual": "",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"subId": null,
|
||||
"content": "swap positions",
|
||||
"procedure": "1. 청크의 위치를 바꾼다.",
|
||||
"testData": null,
|
||||
"expected": "",
|
||||
"actual": "",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"id": 27,
|
||||
"subId": 1,
|
||||
"content": "edit",
|
||||
"procedure": "1. 청크를 수정한다.",
|
||||
"testData": null,
|
||||
"expected": "",
|
||||
"actual": "",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"id": 27,
|
||||
"subId": 2,
|
||||
"content": "edit chunk conflict",
|
||||
"procedure": "1. 청크를 수정모드에 들어간다.",
|
||||
"testData": null,
|
||||
"expected": "",
|
||||
"actual": "",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"subId": null,
|
||||
"content": "view Chunk",
|
||||
"procedure": "1. 문서를 열어 청크가 렌더링되는지 본다.",
|
||||
"testData": "test.syd",
|
||||
"expected": "",
|
||||
"actual": "",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"subId": null,
|
||||
"content": "add/delete tag",
|
||||
"procedure": "1. 문서에 태그를 추가한다.<br>2. 문서에 태그를 삭제한다.",
|
||||
"testData": "A",
|
||||
"expected": "",
|
||||
"actual": "",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"subId": null,
|
||||
"content": "Drag And Drop Upload,",
|
||||
"procedure": "1. 텍스트를 드래그한다.",
|
||||
"testData": null,
|
||||
"expected": "",
|
||||
"actual": "",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"subId": null,
|
||||
"content": "create/delete/rename file",
|
||||
"procedure": "1. 파일을 만든다.",
|
||||
"testData": "test.txt",
|
||||
"expected": "",
|
||||
"actual": "",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"subId": null,
|
||||
"content": "upload/download files",
|
||||
"procedure": "1. 파일을 업로드한다.",
|
||||
"testData": "test.txt",
|
||||
"expected": "",
|
||||
"actual": "",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"subId": null,
|
||||
"content": "export document",
|
||||
"procedure": "1. export 버튼을 누른다.",
|
||||
"testData": null,
|
||||
"expected": "",
|
||||
"actual": "",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"id": 17,
|
||||
"subId": null,
|
||||
"content": "render",
|
||||
"procedure": "1. 스태시가 그려지는지 확인한다",
|
||||
"testData": null,
|
||||
"expected": "",
|
||||
"actual": "",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"subId": null,
|
||||
"content": "add",
|
||||
"procedure": "1. 청크를 추가한다",
|
||||
"testData": null,
|
||||
"expected": "",
|
||||
"actual": "",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"subId": null,
|
||||
"content": "remove",
|
||||
"procedure": "1. 청크를 삭제한다",
|
||||
"testData": null,
|
||||
"expected": "",
|
||||
"actual": "",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
"subId": null,
|
||||
"content": "Drag and Drop to Document",
|
||||
"procedure": "1. 청크로부터 문서로 청크를 옮긴다.",
|
||||
"testData": null,
|
||||
"expected": "",
|
||||
"actual": "",
|
||||
"pass": true
|
||||
},
|
||||
{
|
||||
"id": 22,
|
||||
"subId": null,
|
||||
"content": "Login",
|
||||
"procedure": "1. 비밀번호를 입력한다.",
|
||||
"testData": "admin",
|
||||
"expected": "",
|
||||
"actual": "",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"id": 24,
|
||||
"subId": null,
|
||||
"content": "Localization",
|
||||
"procedure": "1. 다른언어를 지원하는지 언어를 바꿔 확인한다",
|
||||
"testData": null,
|
||||
"expected": "",
|
||||
"actual": "",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"subId": null,
|
||||
"content": "Document Search",
|
||||
"procedure": "1. 검색해본다.",
|
||||
"testData": null,
|
||||
"expected": "",
|
||||
"actual": "",
|
||||
"pass": false
|
||||
},
|
||||
{
|
||||
"id": 85,
|
||||
"subId": null,
|
||||
"content": "Permission",
|
||||
"procedure": "1. 각각의 기능들에 권한없이 시도한다",
|
||||
"testData": null,
|
||||
"expected": "",
|
||||
"actual": "",
|
||||
"pass": true
|
||||
}
|
||||
]
|
||||
|
||||
import {Issue} from "./githubType.ts"
|
||||
|
||||
async function readContent(path?: string): Promise<string> {
|
||||
let content = "[]";
|
||||
if (path) {
|
||||
content = await Deno.readTextFile(path);
|
||||
}
|
||||
else throw new Error("No input provided. path or stdin.");
|
||||
return content;
|
||||
}
|
||||
|
||||
const data = await readContent("../build/issues.json")
|
||||
|
||||
const issues = JSON.parse(data) as Issue[]
|
||||
const table = new Map<string, Issue[]>();
|
||||
issues.forEach((x)=>{
|
||||
const category = x.title.split(":")[0];
|
||||
if(!category) return;
|
||||
let c = table.get(category)
|
||||
if(!c){
|
||||
c = [];
|
||||
table.set(category,c);
|
||||
}
|
||||
c.push(x);
|
||||
})
|
||||
|
||||
const keys = Array.from(table.keys());
|
||||
|
||||
|
||||
keys.forEach(x=>{
|
||||
console.log(`\n### ${x}\n`);
|
||||
const issues = table.get(x);
|
||||
console.log("<table>");
|
||||
console.log("<thead>");
|
||||
console.log("<tr>");
|
||||
//console.log("<th>Category</th>");
|
||||
console.log("<th>ID</th>");
|
||||
console.log("<th>Content</th>");
|
||||
console.log("<th>Procedure</th>");
|
||||
console.log("<th>Test Data</th>");
|
||||
console.log("<th>P/F</th>");
|
||||
console.log("</tr>");
|
||||
console.log("</thead>");
|
||||
console.log("<tbody>");
|
||||
const ts = issues!.map(x=> testcase.filter(y=>y.id==x.number)).flat() as Testcase[];
|
||||
|
||||
if(ts?.length == 0) return;
|
||||
|
||||
//console.log(`<tr><th rowspan="${ts?.length}">${x}</th>`);
|
||||
|
||||
ts.forEach((y,i)=>{
|
||||
//if(i>0)
|
||||
console.log("<tr>");
|
||||
const id = y.subId ? `${y.id}-${y.subId}` : y.id;
|
||||
console.log(`<td>${id}</td>`);
|
||||
console.log(`<td>${y.content}</td>`);
|
||||
console.log(`<td>${y.procedure}</td>`);
|
||||
console.log(`<td>${y.testData ?? ""}</td>`);
|
||||
console.log(`<td>${y.pass ? "P" : "F"}</td>`);
|
||||
console.log("</tr>");
|
||||
})
|
||||
console.log("</tbody>");
|
||||
console.log("</table>");
|
||||
})
|
@ -1,28 +0,0 @@
|
||||
interface Reactive<T>{
|
||||
readonly value: T;
|
||||
update: () => Promise<void>;
|
||||
wireTo(...r: Reactive<unknown>[]): void;
|
||||
unwireTo(r: Reactive<unknown>): void;
|
||||
}
|
||||
|
||||
export async function createReactive<T>(fn: () => Promise<T>): Promise<Reactive<T>> {
|
||||
let v = await fn();
|
||||
let listeners: Reactive<unknown>[] = [];
|
||||
|
||||
return {
|
||||
get value() : T {
|
||||
return v;
|
||||
},
|
||||
async update(){
|
||||
const ret = await fn();
|
||||
v = ret;
|
||||
await Promise.all(listeners.map(o => o.update()));
|
||||
},
|
||||
wireTo(...r: Reactive<unknown>[]) {
|
||||
listeners.push(...r);
|
||||
},
|
||||
unwireTo(r: Reactive<unknown>) {
|
||||
listeners = listeners.filter(o => o !== r);
|
||||
}
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user