diff --git a/Makefile b/Makefile index ab6cf62..6a0bca2 100644 --- a/Makefile +++ b/Makefile @@ -53,9 +53,9 @@ format: generate-tests: @echo "Generating tests for all exercises..." @for exercise in $(EXERCISES); do \ - if [ -f exercises/practice/$$exercise/.meta/generateTests.js ]; then \ + if [ -f exercises/practice/$$exercise/.meta/testTemplate.js ]; then \ echo "-> Generating: $$exercise"; \ - node exercises/practice/$$exercise/.meta/generateTests.js || exit 1; \ + node exercises/practice/$$exercise/.meta/testTemplate.js || exit 1; \ else \ echo "-> Skipping: $$exercise (no generator found)"; \ fi \ diff --git a/config.json b/config.json index d61f665..982ebc4 100644 --- a/config.json +++ b/config.json @@ -44,6 +44,14 @@ "practices": [], "prerequisites": [], "difficulty": 1 + }, + { + "slug": "two-fer", + "name": "Two-Fer", + "uuid": "2a03dbf7-5aae-4859-8fde-b51c11cab02f", + "practices": [], + "prerequisites": [], + "difficulty": 2 } ] }, diff --git a/exercises/practice/hello-world/.meta/generateTests.js b/exercises/practice/hello-world/.meta/generateTests.js deleted file mode 100644 index 620c69a..0000000 --- a/exercises/practice/hello-world/.meta/generateTests.js +++ /dev/null @@ -1,11 +0,0 @@ -import { fileURLToPath } from 'node:url'; -import path from 'node:path'; -import getValidCases from '../../../../test_generator/getCases.js'; -import { generate, toPascalCase } from '../../../../test_generator/testGenerator.js'; -import * as template from './testTemplate.js'; - -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const cases = getValidCases(template.slug); -const outputPath = path.resolve(__dirname, '..', 'tests', `${toPascalCase(template.slug)}_test.res`); - -generate(outputPath, template.slug, cases, template); \ No newline at end of file diff --git a/exercises/practice/hello-world/.meta/testTemplate.js b/exercises/practice/hello-world/.meta/testTemplate.js index 4767482..0db698e 100644 --- a/exercises/practice/hello-world/.meta/testTemplate.js +++ b/exercises/practice/hello-world/.meta/testTemplate.js @@ -1,8 +1,17 @@ -export const slug = 'hello-world'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { stringEqual } from "../../../../test_generator/assertions.js"; +import { generateTests } from '../../../../test_generator/testGenerator.js'; -export const assertionFunctions = `let stringEqual = (~message=?, a: string, b: string) => - assertion(~message?, ~operator="stringEqual", (a, b) => a == b, a, b)`; +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const slug = path.basename(path.resolve(__dirname, '..')) -export const template = (c, moduleName) => { - return `stringEqual(~message="${c.description}", ${moduleName}.hello(), "${c.expected}")` -} \ No newline at end of file +// EDIT THIS WITH YOUR ASSERTIONS +export const assertionFunctions = [ stringEqual ] + +// EDIT THIS WITH YOUR TEST TEMPLATES +export const template = (c) => { + return `stringEqual(~message="${c.description}", hello(), "${c.expected}")` +} + +generateTests(__dirname, slug, assertionFunctions, template) \ No newline at end of file diff --git a/exercises/practice/hello-world/tests/HelloWorld_test.res b/exercises/practice/hello-world/tests/HelloWorld_test.res index 0192f17..2e080e8 100644 --- a/exercises/practice/hello-world/tests/HelloWorld_test.res +++ b/exercises/practice/hello-world/tests/HelloWorld_test.res @@ -1,8 +1,9 @@ open Test +open HelloWorld -let stringEqual = (~message=?, a: string, b: string) => - assertion(~message?, ~operator="stringEqual", (a, b) => a == b, a, b) +let stringEqual = (~message=?, a: string, b: string) => assertion(~message?, ~operator="stringEqual", (a, b) => a == b, a, b) test("Say Hi!", () => { - stringEqual(~message="Say Hi!", HelloWorld.hello(), "Hello, World!") -}) \ No newline at end of file + stringEqual(~message="Say Hi!", hello(), "Hello, World!") +}) + diff --git a/exercises/practice/two-fer/.docs/instructions.md b/exercises/practice/two-fer/.docs/instructions.md new file mode 100644 index 0000000..adc5348 --- /dev/null +++ b/exercises/practice/two-fer/.docs/instructions.md @@ -0,0 +1,24 @@ +# Instructions + +Your task is to determine what you will say as you give away the extra cookie. + +If you know the person's name (e.g. if they're named Do-yun), then you will say: + +```text +One for Do-yun, one for me. +``` + +If you don't know the person's name, you will say _you_ instead. + +```text +One for you, one for me. +``` + +Here are some examples: + +| Name | Dialogue | +| :----- | :-------------------------- | +| Alice | One for Alice, one for me. | +| Bohdan | One for Bohdan, one for me. | +| | One for you, one for me. | +| Zaphod | One for Zaphod, one for me. | diff --git a/exercises/practice/two-fer/.docs/introduction.md b/exercises/practice/two-fer/.docs/introduction.md new file mode 100644 index 0000000..5947a22 --- /dev/null +++ b/exercises/practice/two-fer/.docs/introduction.md @@ -0,0 +1,8 @@ +# Introduction + +In some English accents, when you say "two for" quickly, it sounds like "two fer". +Two-for-one is a way of saying that if you buy one, you also get one for free. +So the phrase "two-fer" often implies a two-for-one offer. + +Imagine a bakery that has a holiday offer where you can buy two cookies for the price of one ("two-fer one!"). +You take the offer and (very generously) decide to give the extra cookie to someone else in the queue. diff --git a/exercises/practice/two-fer/.meta/TwoFer.res b/exercises/practice/two-fer/.meta/TwoFer.res new file mode 100644 index 0000000..73db48c --- /dev/null +++ b/exercises/practice/two-fer/.meta/TwoFer.res @@ -0,0 +1,6 @@ +let twoFer = name => { + switch name { + | None => "One for you, one for me." + | Some(nameString) => "One for " ++ nameString ++ ", one for me." + } +} diff --git a/exercises/practice/two-fer/.meta/config.json b/exercises/practice/two-fer/.meta/config.json new file mode 100644 index 0000000..ab9fae2 --- /dev/null +++ b/exercises/practice/two-fer/.meta/config.json @@ -0,0 +1,22 @@ +{ + "authors": [ + "tejasbubane", + "therealowenrees" + ], + "files": { + "solution": [ + "src/TwoFer.res" + ], + "test": [ + "tests/TwoFer_test.res" + ], + "example": [ + ".meta/TwoFer.res" + ], + "editor": [ + "src/TwoFer.resi" + ] + }, + "blurb": "Create a sentence of the form \"One for X, one for me.\".", + "source_url": "https://github.com/exercism/problem-specifications/issues/757" +} diff --git a/exercises/practice/two-fer/.meta/testTemplate.js b/exercises/practice/two-fer/.meta/testTemplate.js new file mode 100644 index 0000000..94e90a9 --- /dev/null +++ b/exercises/practice/two-fer/.meta/testTemplate.js @@ -0,0 +1,21 @@ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { stringEqual } from "../../../../test_generator/assertions.js"; +import { generateTests } from '../../../../test_generator/testGenerator.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const slug = path.basename(path.resolve(__dirname, '..')) + +// EDIT THIS WITH YOUR ASSERTIONS +export const assertionFunctions = [ stringEqual ] + +// EDIT THIS WITH YOUR TEST TEMPLATES +export const template = (c) => { + if (c.input.name) { + return `stringEqual(~message="${c.description}", twoFer(Some("${c.input.name}")), "${c.expected}")` + } else { + return `stringEqual(~message="${c.description}", twoFer(None), "${c.expected}")` + } +} + +generateTests(__dirname, slug, assertionFunctions, template) \ No newline at end of file diff --git a/exercises/practice/two-fer/.meta/tests.toml b/exercises/practice/two-fer/.meta/tests.toml new file mode 100644 index 0000000..d0e3857 --- /dev/null +++ b/exercises/practice/two-fer/.meta/tests.toml @@ -0,0 +1,19 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[1cf3e15a-a3d7-4a87-aeb3-ba1b43bc8dce] +description = "no name given" + +[b4c6dbb8-b4fb-42c2-bafd-10785abe7709] +description = "a name given" + +[3549048d-1a6e-4653-9a79-b0bda163e8d5] +description = "another name given" diff --git a/exercises/practice/two-fer/rescript.json b/exercises/practice/two-fer/rescript.json new file mode 100644 index 0000000..3db8c50 --- /dev/null +++ b/exercises/practice/two-fer/rescript.json @@ -0,0 +1,15 @@ +{ + "name": "@exercism/rescript", + "sources": [ + { "dir": "src", "subdirs": true, "type": "dev" }, + { "dir": "tests", "subdirs": true, "type": "dev" } + ], + "package-specs": [ + { + "module": "esmodule", + "in-source": true + } + ], + "suffix": ".res.js", + "dev-dependencies": ["rescript-test"] +} diff --git a/exercises/practice/two-fer/src/TwoFer.res b/exercises/practice/two-fer/src/TwoFer.res new file mode 100644 index 0000000..28ece47 --- /dev/null +++ b/exercises/practice/two-fer/src/TwoFer.res @@ -0,0 +1 @@ +let twoFer = _ => panic("'twoFer not yet implemented") diff --git a/exercises/practice/two-fer/src/TwoFer.resi b/exercises/practice/two-fer/src/TwoFer.resi new file mode 100644 index 0000000..62f49c2 --- /dev/null +++ b/exercises/practice/two-fer/src/TwoFer.resi @@ -0,0 +1 @@ +let twoFer: option => string diff --git a/exercises/practice/two-fer/tests/TwoFer_test.res b/exercises/practice/two-fer/tests/TwoFer_test.res new file mode 100644 index 0000000..128a4f1 --- /dev/null +++ b/exercises/practice/two-fer/tests/TwoFer_test.res @@ -0,0 +1,17 @@ +open Test +open TwoFer + +let stringEqual = (~message=?, a: string, b: string) => assertion(~message?, ~operator="stringEqual", (a, b) => a == b, a, b) + +test("no name given", () => { + stringEqual(~message="no name given", twoFer(None), "One for you, one for me.") +}) + +test("a name given", () => { + stringEqual(~message="a name given", twoFer(Some("Alice")), "One for Alice, one for me.") +}) + +test("another name given", () => { + stringEqual(~message="another name given", twoFer(Some("Bob")), "One for Bob, one for me.") +}) + diff --git a/problem-specifications b/problem-specifications new file mode 160000 index 0000000..0ebef3c --- /dev/null +++ b/problem-specifications @@ -0,0 +1 @@ +Subproject commit 0ebef3c03248e718b261815dc703c1a1600630f0 diff --git a/test_generator/assertions.js b/test_generator/assertions.js new file mode 100644 index 0000000..cce70b6 --- /dev/null +++ b/test_generator/assertions.js @@ -0,0 +1 @@ +export const stringEqual = `let stringEqual = (~message=?, a: string, b: string) => assertion(~message?, ~operator="stringEqual", (a, b) => a == b, a, b)`; \ No newline at end of file diff --git a/test_generator/testGenerator.js b/test_generator/testGenerator.js index 55c9417..ac7730a 100644 --- a/test_generator/testGenerator.js +++ b/test_generator/testGenerator.js @@ -1,20 +1,30 @@ import fs from 'node:fs'; import path from 'node:path'; -import { template } from '../exercises/practice/hello-world/.meta/testTemplate.js'; +import getValidCases from './getCases.js'; -export const toPascalCase = (slug) => +export const generateTests = (dir, slug, assertionFunctions, template) => { + const outputPath = path.resolve(dir, '..', 'tests', `${toPascalCase(slug)}_test.res`); + const cases = getValidCases(slug); + generate(outputPath, slug, cases, assertionFunctions, template); +} + +const toPascalCase = (slug) => slug.split('-').map(w => w.charAt(0).toUpperCase() + w.slice(1)).join(''); -export const generate = (outputPath, slug, cases, config) => { +const generate = (outputPath, slug, cases, assertionFunctions, template) => { const moduleName = toPascalCase(slug); - let output = `open Test\n\n`; - output += `${config.assertionFunctions}\n\n`; + let output = `open Test +open ${moduleName}\n\n`; + + if (Array.isArray(assertionFunctions)) { + output += assertionFunctions.map(fn => fn.trim()).join('\n\n') + '\n\n'; + } cases.forEach((c) => { output += `test("${c.description}", () => { - ${template(c, moduleName)} -})` + ${template(c)} +})\n\n` }); if (!fs.existsSync(path.dirname(outputPath))) {