Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ Add a `source.json` file with this structure in the `data` folder:
{
"items": [
{
"id": "The money",
"id": "My beautiful sentence",
"card": {
"front": {
"main": "The Money",
"main": "My beautiful sentence"
},
"back": {
"main": "L'argent",
"main": "Ma belle phrase"
}
}
}
Expand All @@ -46,11 +46,9 @@ npm start
## Next steps
* Add a "help" scene.
* Add colors.
* Add a possibility to select the "select card" strategy.
* Add a possibility to select the strategy.
* Add a possibility to start up with a configuration (args)
* Add more choice, random order, time, challenge....
* Add a possibility to edit answers.
* Release and run it without compiling it.
* Move to deno ?
* Add a json schema and validate source.
* Add import source script.
183 changes: 183 additions & 0 deletions data/test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
{
"items": [
{
"id": "irrelevant sein",
"card": {
"front": {
"main": "irrelevant sein test1",
"variations": []
},
"back": {
"main": "être sans importance test1",
"variations": [
"être hors sujet test1",
"être sans rapport test1",
"être non pertinent test1"
]
}
},
"last_revision": "2024-01-28T18:34:53.155Z",
"categories": [],
"revision_count": 1,
"favorite_lvl": 0,
"errors_last": 0,
"errors_total": 0
},
{
"id": "test2",
"card": {
"front": {
"main": "test2",
"variations": []
},
"back": {
"main": "test2",
"variations": []
}
},
"last_revision": "2024-01-29T18:34:53.155Z",
"categories": [],
"revision_count": 1,
"favorite_lvl": 0,
"errors_last": 0,
"errors_total": 0
},
{
"id": "jemanden aussprechen lassen",
"card": {
"front": {
"main": "jemanden aussprechen lassen",
"variations": [
"jemanden ausreden lassen"
]
},
"back": {
"main": "laisser finir quelqu'un de parler",
"variations": []
}
},
"last_revision": "2024-01-30T18:34:53.155Z",
"categories": [],
"revision_count": 1,
"favorite_lvl": 0,
"errors_last": 0,
"errors_total": 0
},
{
"id": "den Faden verlieren",
"card": {
"front": {
"main": "den Faden verlieren",
"variations": []
},
"back": {
"main": "perdre le fil",
"variations": []
}
},
"last_revision": "2024-01-31T09:15:04.689Z",
"categories": [],
"revision_count": 1,
"favorite_lvl": 0,
"errors_last": 1,
"errors_total": 1
},
{
"id": "Worauf wollen Sie hinaus?",
"card": {
"front": {
"main": "Worauf wollen Sie hinaus?",
"variations": []
},
"back": {
"main": "Où voulez-vous en venir ?",
"variations": []
}
},
"last_revision": "2024-02-01T09:08:04.789Z",
"categories": [],
"revision_count": 2,
"favorite_lvl": 0,
"errors_last": 1,
"errors_total": 1
},
{
"id": "bis zu einem bestimmten Punkt",
"card": {
"front": {
"main": "bis zu einem bestimmten Punkt",
"variations": []
},
"back": {
"main": "jusqu'à un certain point",
"variations": []
}
},
"last_revision": "2024-02-02T09:08:04.789Z",
"categories": [],
"revision_count": 1,
"favorite_lvl": 0,
"errors_last": 0,
"errors_total": 0
},
{
"id": "Kritik äußern",
"card": {
"front": {
"main": "Kritik äußern",
"variations": []
},
"back": {
"main": "critiquer",
"variations": []
}
},
"last_revision": "2024-02-03T12:25:56.074Z",
"categories": [],
"revision_count": 1,
"favorite_lvl": 0,
"errors_last": 0,
"errors_total": 0
},
{
"id": "mit jemandem offen sprechen",
"card": {
"front": {
"main": "mit jemandem offen sprechen",
"variations": []
},
"back": {
"main": "parler ouvertement à quelqu'un",
"variations": []
}
},
"last_revision": "2024-02-04T12:25:56.074Z",
"categories": [],
"revision_count": 1,
"favorite_lvl": 0,
"errors_last": 0,
"errors_total": 0
},
{
"id": "effizienter arbeiten",
"card": {
"front": {
"main": "effizienter arbeiten",
"variations": []
},
"back": {
"main": "travailler plus efficacement",
"variations": [
"travailler de manière plus efficace"
]
}
},
"last_revision": "2024-02-05T12:25:56.074Z",
"categories": [],
"revision_count": 1,
"favorite_lvl": 0,
"errors_last": 0,
"errors_total": 0
}
]
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"lint": "eslint src/*.ts src/**/*.ts",
"prettier": "prettier 'src/{**/*,*}.{js,ts,tsx}' --write",
"test": "vitest run",
"test-debug": "vitest run --inspect-brk --testTimeout=0 --no-file-parallelism",
"start": "node dist/src/run.js",
"debug": "node --inspect dist/src/run.js"
},
Expand Down
61 changes: 61 additions & 0 deletions src/cli-learning-cards.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { test, describe, vi, beforeAll, expect } from "vitest";
import { stdin } from "node:process";
import { CliLearningCards } from "./cli-learning-cards.js";

const consoleSpy = vi.spyOn(console, "log");
const getLastLog = () => `${consoleSpy.mock.lastCall}`;
/** @returns n previous log */
const getReverseLog = (reverseIndex: number) => {
const n = consoleSpy.mock.calls.length - reverseIndex;
return `${consoleSpy.mock.calls[n]}`;
};
const timeout = (ms: number) => {
return new Promise((resolve) => setTimeout(resolve, ms));
};

describe("Cli-learning-card", () => {
beforeAll(() => {
consoleSpy.mockClear();
});

test("The whole process", async () => {
const clc = new CliLearningCards(
new URL("../data/test.json", import.meta.url),
);
clc.run();
expect(getLastLog().includes("Cli-learning-cards")).toBeTruthy();
stdin.emit("keypress", null, { name: "enter" });
await timeout(100);
expect(getLastLog().includes("> Ten cards")).toBeTruthy();
stdin.emit("keypress", null, { name: "up" });
stdin.emit("keypress", null, { name: "up" });
expect(getLastLog().includes("> Lives")).toBeTruthy();
stdin.emit("keypress", null, { name: "enter" });
await timeout(100);
expect(getReverseLog(2).includes("test1")).toBeTruthy();
//clc.getRl().write("_hint"); // why this doesn't work !!?
clc.write("_hint");
await timeout(100);
clc.write("_");
await timeout(100);
expect(
getReverseLog(2).includes("This command is not valid."),
).toBeTruthy();
clc.write("_skip");
await timeout(100);
expect(getReverseLog(2).includes("test2")).toBeTruthy();
clc.write("test2");
await timeout(100);
clc.write("_exit");
expect(
getReverseLog(2).includes("Do you want to save the results (y/n) ?"),
).toBeTruthy();
clc.write("n");
await timeout(100);
expect(getReverseLog(2).includes("left unsaved.")).toBeTruthy();
// Vitest doesn't like the "process.exit(0)" on close stream, skip the very end of the game;
//clc.testRl("\n");
//await timeout(100);
//expect(getLastLog().includes("Bye")).toBeTruthy();
});
});
13 changes: 12 additions & 1 deletion src/cli-learning-cards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { GameStateScene } from "./enums.js";
* Cli Learning cards main process.
*/
export class CliLearningCards {
private rl: readline.Interface;
private readonly rl: readline.Interface;
private scene?: Scene;

constructor(sourcePath: URL) {
Expand Down Expand Up @@ -60,6 +60,17 @@ export class CliLearningCards {
this.startStream();
}

/**
* For testing purpose, ask the rl to write (same process,
* same rl instance to go to the listeners).
* Doing this by another way, like returning "rl" to let the
* tests calling the "write" method, doesn't work for
* an unknown reason.
*/
write(line: string) {
this.rl.write(`${line}\n`);
}

/**
* Activate another scene based on the GameStateScene.
* Auto call "start" on scene change.
Expand Down
2 changes: 1 addition & 1 deletion src/scenes/card-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
* @returns the ideal width of cards.
*/
export const getCardWidth = (terminalWidth: number): number => {
return Math.min(terminalWidth, 50);
return Math.max(Math.min(terminalWidth, 50) || 0, 25);
};