Skip to content

Commit

Permalink
added a tutorial-and-solution-generator; added tons of tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Nickname5862 committed Jul 18, 2024
1 parent eef2e33 commit bf9c621
Show file tree
Hide file tree
Showing 41 changed files with 7,398 additions and 906 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.

TODO: should I put my shit in?

## [8.0.0](https://github.com/skunkteam/sherlock/compare/v7.0.0...v8.0.0) (2023-10-17)

### ⚠ BREAKING CHANGES
Expand Down
88 changes: 88 additions & 0 deletions generateTutorialAndSolution.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
// import * as fs from 'fs';
var fs = require("node:fs/promises");
// Run with: tsc generateTutorialAndSolution.ts && node generateTutorialAndSolution.js
function generateTutorialAndSolutions() {
return __awaiter(this, void 0, void 0, function () {
var filenames, _i, filenames_1, filename, originalContent, tutorialContent, solutionContent;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, fs.readdir('generator')];
case 1:
filenames = (_a.sent()).filter(function (f) { return f.endsWith("test.ts"); });
_i = 0, filenames_1 = filenames;
_a.label = 2;
case 2:
if (!(_i < filenames_1.length)) return [3 /*break*/, 7];
filename = filenames_1[_i];
return [4 /*yield*/, fs.readFile("generator/".concat(filename), 'utf8')];
case 3:
originalContent = _a.sent();
tutorialContent = originalContent
.replace(/describe(?!\.skip)/g, "describe.skip") // change `describe` to `describe.skip`
.replace(/\/\/ #QUESTION-BLOCK-(START|END)/g, "")
.replace(/\/\/ #QUESTION/g, "") // remove `// #QUESTION` comments
.replace(/\/\/ #ANSWER-BLOCK-START[\s\S]*?\/\/ #ANSWER-BLOCK-END/g, "") // remove // #ANSWER blocks
.replace(/\n.*?\/\/ #ANSWER/g, "") // remove the entire `// #ANSWER` line, including comment
.replace(/\n\s*\n\s*\n/g, "\n\n");
return [4 /*yield*/, fs.writeFile("generated_tutorial/".concat(filename), tutorialContent)];
case 4:
_a.sent();
solutionContent = originalContent
.replace(/describe\.skip/g, "describe") // change `describe.skip` to `describe`
.replace(/\/\/ #ANSWER-BLOCK-(START|END)/g, "")
.replace(/\/\/ #ANSWER/g, "") // remove `// #ANSWER` comments
.replace(/\/\/ #QUESTION-BLOCK-START[\s\S]*?\/\/ #QUESTION-BLOCK-END/g, "") // remove // #QUESTION blocks
.replace(/\n.*?\/\/ #QUESTION/g, "") // remove the entire `// #QUESTION` line, including comment
.replace(/\n\s*\n\s*\n/g, "\n\n");
return [4 /*yield*/, fs.writeFile("generated_solution/".concat(filename), solutionContent)];
case 5:
_a.sent();
console.log("\u001B[33m ".concat(filename, " saved! \u001B[0m"));
_a.label = 6;
case 6:
_i++;
return [3 /*break*/, 2];
case 7: return [2 /*return*/];
}
});
});
}
generateTutorialAndSolutions();
39 changes: 39 additions & 0 deletions generateTutorialAndSolution.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// import * as fs from 'fs';
import * as fs from 'node:fs/promises';

// Run with: tsc generateTutorialAndSolution.ts && node generateTutorialAndSolution.js

async function generateTutorialAndSolutions() {
// get all filenames in the folder 'tutorial' that end on '.ts'
let filenames: string[] = (await fs.readdir('generator')).filter(f => f.endsWith(`test.ts`));

for (let filename of filenames) {
// // Read the original text file
const originalContent = await fs.readFile(`generator/${filename}`, 'utf8');

// ** TUTORIAL **
let tutorialContent = originalContent
.replace(/describe(?!\.skip)/g, `describe.skip`) // change `describe` to `describe.skip`
.replace(/\/\/ #QUESTION-BLOCK-(START|END)/g, ``)
.replace(/\/\/ #QUESTION/g, ``) // remove `// #QUESTION` comments
.replace(/\/\/ #ANSWER-BLOCK-START[\s\S]*?\/\/ #ANSWER-BLOCK-END/g, ``) // remove // #ANSWER blocks
.replace(/\n.*?\/\/ #ANSWER/g, ``) // remove the entire `// #ANSWER` line, including comment
.replace(/\n\s*\n\s*\n/g, `\n\n`); // remove excess whitespaces/newlines

await fs.writeFile(`generated_tutorial/${filename}`, tutorialContent);

// ** SOLUTION **
let solutionContent = originalContent
.replace(/describe\.skip/g, `describe`) // change `describe.skip` to `describe`
.replace(/\/\/ #ANSWER-BLOCK-(START|END)/g, ``)
.replace(/\/\/ #ANSWER/g, ``) // remove `// #ANSWER` comments
.replace(/\/\/ #QUESTION-BLOCK-START[\s\S]*?\/\/ #QUESTION-BLOCK-END/g, ``) // remove // #QUESTION blocks
.replace(/\n.*?\/\/ #QUESTION/g, ``) // remove the entire `// #QUESTION` line, including comment
.replace(/\n\s*\n\s*\n/g, `\n\n`); // remove excess whitespaces/newlines

await fs.writeFile(`generated_solution/${filename}`, solutionContent);
console.log(`\x1b[33m ${filename} saved! \x1b[0m`);
}
}

generateTutorialAndSolutions();
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ describe('intro', () => {
--- Welcome to the tutorial! ---
Please look in \`./tutorial/1 - intro.test.ts\` to see what to do next.`, () => {
// TODO:
// At the start of the spec, there will be some setup.
let bool = false;

Expand All @@ -44,7 +43,7 @@ describe('intro', () => {
* This can also be indicated with the `__YOUR_TURN__` variable.
*
* It should be clear what to do here... */
bool = true;
bool = true;
expect(bool).toBeTrue();
// We use expectations like this to verify the result.
});
Expand Down Expand Up @@ -72,7 +71,7 @@ describe('the basics', () => {
// the `Atom`.
expect(myValue$.get()).toEqual(1);

myValue$.set(2);
myValue$.set(2);
// Use the `.set(<newValue>)` method to change the value of the `Atom`.
expect(myValue$.get()).toEqual(2);
});
Expand All @@ -98,7 +97,7 @@ describe('the basics', () => {
* negative to a positive number and vice versa) of the original `Atom`.
*/
// Use `myValue$.derive(val => ...)` to implement `myInverse$`.
const myInverse$ = myValue$.derive(val => -val);
const myInverse$ = myValue$.derive(val => -val);
expect(myInverse$.get()).toEqual(-1);
// So if we set `myValue$` to -2:
myValue$.set(-2);
Expand All @@ -123,7 +122,7 @@ describe('the basics', () => {
*
* Now react to `myCounter$`. In every `react()`.
* Increase the `reacted` variable by one. */
myCounter$.react(() => reacted++);
myCounter$.react(() => reacted++);
expect(reacted).toEqual(1);
// `react()` will react immediately, more on that later.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,15 @@ describe('deriving', () => {
* method on another `Derivable`.
*/

// Should return 'Buzz' when `myCounter$` is a multiple of 5 and '' otherwise.

// Should return 'Fizz' when `myCounter$` is a multiple of 3 and '' otherwise.
const fizz$: Derivable<string> = myCounter$.derive(v => (v % 3 === 0 ? 'Fizz' : ''));
const fizz$: Derivable<string> = myCounter$.derive(v => (v % 3 ? 'Fizz' : '')); // Shorthand for `v % 3 === 0`

// Should return 'Buzz' when `myCounter$` is a multiple of 5 and '' otherwise.
const buzz$: Derivable<string> = myCounter$.derive(v => (v % 5 === 0 ? 'Buzz' : ''));
const buzz$: Derivable<string> = myCounter$.derive(v => (v % 5 ? 'Buzz' : ''));

const fizzBuzz$: Derivable<string | number> = derive(
() => (fizz$.get() + buzz$.get() === '' ? myCounter$.get() : fizz$.get() + buzz$.get()), // TODO: why not put it on the counter then?
);
const fizzBuzz$: Derivable<string | number> = derive(() => fizz$.get() + buzz$.get() || myCounter$.get());

expect(fizz$.get()).toEqual('');
expect(buzz$.get()).toEqual('');
Expand Down Expand Up @@ -157,7 +157,7 @@ describe('deriving', () => {

expect(tweetCount).toEqual(3); // Is there a new tweet?
expect(lastTweet).toContain('Donald'); // Who sent it? Donald? Or Barack?
expect(lastTweet).toContain('race'); // What did he tweet?
expect(lastTweet).toContain('politics'); // What did he tweet?

/**
* As you can see, this is something to look out for.
Expand Down Expand Up @@ -200,6 +200,7 @@ describe('deriving', () => {
* `.and(...)` and `.or(...)`. Make sure the output of those `Derivable`s
* is either 'Fizz'/'Buzz' or ''.
*/

const fizz$ = myCounter$
.derive(count => count % 3)
.is(0)
Expand All @@ -212,12 +213,13 @@ describe('deriving', () => {
.and('Buzz')
.or('');

const fizzBuzz$ = derive(() => fizz$.get() + buzz$.get()).or(myCounter$); // TODO:
const fizzBuzz$ = derive(() => fizz$.get() + buzz$.get()).or(myCounter$);
// This will check whether `fizz$.get() + buzz$.get()` is truthy: if so, return it; if not, return `myCounter$`

for (let count = 1; count <= 100; count++) {
// Set the value of the `Atom`,
myCounter$.set(count);
// console.log(myCounter$.get() + ', ' + fizzBuzz$.get());

// and check if the output changed accordingly.
checkFizzBuzz(count, fizzBuzz$.get());
}
Expand Down
Loading

0 comments on commit bf9c621

Please sign in to comment.