Skip to content

Commit

Permalink
feat(command/init): setup test with vitest
Browse files Browse the repository at this point in the history
  • Loading branch information
EmileRolley committed Oct 3, 2024
1 parent 92baef5 commit bb364df
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 117 deletions.
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,9 @@
},
"devDependencies": {
"@oclif/test": "^4.0.9",
"@types/jest": "^29.2.5",
"@types/jest": "^29.5.13",
"docdash": "^2.0.1",
"prettier": "^3.0.0",
"ts-jest": "^29.0.4",
"ts-node": "^10.9.2",
"tsup": "^8.0.2",
"typedoc": "^0.24.8",
Expand Down
173 changes: 134 additions & 39 deletions src/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { OptionFlag } from '@oclif/core/lib/interfaces'
import { spawn } from 'child_process'

type PackageManager = 'npm' | 'yarn' | 'pnpm' | 'bun'
type ExtraTool = 'gh-actions' | 'test'

export default class Init extends Command {
static override args = {}
Expand Down Expand Up @@ -63,17 +64,38 @@ installation.`,
}),
}

// TODO: refactor to have a unique project object which is built and passed
// to the methods before writing it in one go.
public async run(): Promise<void> {
p.intro(chalk.bgHex('#2975d1')(' publicodes init '))

const { flags } = await this.parse(Init)
const currentDir = path.basename(process.cwd())
const pkgJSON = await getPackageJson(currentDir, flags.yes)
const pkgJSON = await this.getPackageJson(currentDir, flags.yes)

// TODO: check for existing 'test' directory and '.github/workflows' directory
const extraTools = await getExtraTools(flags.yes)
// TODO: factorize this
if (p.isCancel(extraTools)) {
p.cancel('init cancelled')
process.exit(1)
}

this.updatePackageJson(pkgJSON)
if (extraTools.includes('gh-actions')) {
// TODO
// setupGithubActions()
}
if (extraTools.includes('test')) {
setupTests(pkgJSON)
}

const pkgManager = await getPackageManager(flags['pkg-manager'], flags.yes)
// const extraTools = await getExtraTools(flags.yes)
if (p.isCancel(pkgManager)) {
p.cancel('init cancelled')
process.exit(1)
}

await generateBaseFiles(pkgJSON, pkgManager)

const shouldInstall =
flags['no-install'] === undefined && !flags.yes
Expand All @@ -86,8 +108,6 @@ installation.`,
await installDeps(pkgManager)
}

await generateBaseFiles(pkgJSON, pkgManager)

p.note(
`${chalk.bold('You can now:')}
- write your Publicodes rules in ${chalk.bold.yellow('.src/')}
Expand All @@ -100,8 +120,18 @@ installation.`,
)
}

private updatePackageJson(pkgJSON: PackageJson): void {
const packageJsonPath = path.join(process.cwd(), 'package.json')
private async getPackageJson(
currentDir: string,
useDefault: boolean,
): Promise<PackageJson> {
let pkgJSON = readPackageJson()

if (!pkgJSON && useDefault) {
pkgJSON = { ...basePackageJson, name: currentDir }
}
if (!pkgJSON) {
pkgJSON = await askPackageJsonInfo(currentDir)
}

pkgJSON.type = basePackageJson.type
pkgJSON.main = basePackageJson.main
Expand All @@ -128,33 +158,8 @@ installation.`,
pkgJSON.publishConfig = { access: 'public' }
}

try {
fs.writeFileSync(packageJsonPath, JSON.stringify(pkgJSON, null, 2))
p.log.step(`${chalk.bold('package.json')} file written`)
} catch (error) {
exitWithError({
ctx: `An error occurred while writing the ${chalk.bold('package.json')} file`,
msg: error.message,
})
}
}
}

async function getPackageJson(
currentDir: string,
useDefault: boolean,
): Promise<PackageJson> {
const localPkgJson = readPackageJson()

if (localPkgJson) {
return localPkgJson
return pkgJSON
}

if (useDefault) {
return { ...basePackageJson, name: currentDir }
}

return await askPackageJsonInfo(currentDir)
}

async function getPackageManager(
Expand Down Expand Up @@ -237,13 +242,45 @@ function askPackageManager(): Promise<PackageManager> {
}) as Promise<PackageManager>
}

async function getExtraTools(useDefault: boolean): Promise<ExtraTool[]> {
if (useDefault) {
return ['gh-actions', 'test']
}
return p.multiselect({
message: `Select extra tools (press ${chalk.bold.italic('space')} to unselect)`,
options: [
{
value: 'gh-actions',
label: 'GitHub Actions',
hint: 'automate build, test and publishing',
},
{ value: 'test', label: 'Unit test', hint: 'Vitest + example' },
],
required: false,
initialValues: ['gh-actions', 'test'],
}) as Promise<ExtraTool[]>
}

function setupTests(pkgJSON: PackageJson) {
pkgJSON.devDependencies = {
...pkgJSON.devDependencies,
vitest: '^2.1.2',
'@types/jest': '^29.5.13',
}
pkgJSON.scripts = {
...pkgJSON.scripts,
test: 'vitest --globals',
}
return pkgJSON
}

async function installDeps(pkgManager: PackageManager): Promise<void> {
return runAsyncWithSpinner(
'Installing dependencies',
'Dependencies installed',
(spinner: Spinner) => {
return new Promise<void>((resolve) => {
const program = spawn(pkgManager, ['install', '-y'], {
const program = spawn(pkgManager, ['install'], {
stdio: 'ignore',
})

Expand All @@ -258,7 +295,7 @@ async function installDeps(pkgManager: PackageManager): Promise<void> {
program.on('close', (code) => {
if (code !== 0) {
exitWithError({
ctx: `An error occurred while installing dependencies (exec: ${pkgManager} install -y)`,
ctx: `An error occurred while installing dependencies (exec: ${pkgManager} install)`,
msg: `Process exited with code ${code}`,
spinner,
})
Expand All @@ -276,6 +313,10 @@ async function generateBaseFiles(
): Promise<void> {
return runWithSpinner('Generating files', 'Files generated', (spinner) => {
try {
// Generate package.json
const packageJsonPath = path.join(process.cwd(), 'package.json')
fs.writeFileSync(packageJsonPath, JSON.stringify(pjson, null, 2))

// Generate README.md
if (!fs.existsSync('README.md')) {
fs.writeFileSync('README.md', getReadmeContent(pjson, pkgManager))
Expand All @@ -285,9 +326,28 @@ async function generateBaseFiles(
if (!fs.existsSync('src')) {
fs.mkdirSync('src')
}
if (!fs.existsSync('src/base.publicodes')) {
fs.writeFileSync('src/base.publicodes', BASE_PUBLICODES)
if (!fs.existsSync('src/salaire.publicodes')) {
fs.writeFileSync('src/salaire.publicodes', BASE_PUBLICODES)
}
if (!fs.existsSync('.gitignore')) {
try {
execSync('git init', { stdio: 'ignore' })
} catch (error) {
p.log.warn(
`Could not initialize a git repository (make sure ${chalk.bold.italic('git')} is installed)`,
)
}
fs.writeFileSync(
'.gitignore',
`node_modules\n${pjson.files?.join('\n')}`,
)
}

if (!fs.existsSync('test')) {
fs.mkdirSync('test')
}

fs.writeFileSync('test/salaire.test.ts', BASE_TEST_FILE)
} catch (error) {
exitWithError({
ctx: 'An error occurred while generating files',
Expand All @@ -309,7 +369,7 @@ ${pjson.description}
## Installation
\`\`\`sh
npm install ${pjson.name} publicodes
${pkgManager} install ${pjson.name} publicodes
\`\`\`
## Usage
Expand Down Expand Up @@ -337,6 +397,13 @@ ${pkgManager} install
// Compile the Publicodes rules
${pkgManager} run compile
${
pjson.scripts?.test
? `// Run the tests
${pkgManager} run test`
: ''
}
// Run the documentation server
${pkgManager} run doc
\`\`\`
Expand All @@ -350,7 +417,8 @@ salaire net: salaire brut - cotisations salariales
salaire brut:
titre: Salaire brut mensuel
par défaut: 2500 €/mois
par défaut: 2500
unité: €/mois
cotisations salariales:
produit:
Expand All @@ -359,3 +427,30 @@ cotisations salariales:
avec:
taux: 21.7%
`

const BASE_TEST_FILE = `import Engine, { serializeEvaluation, serializeUnit } from "publicodes";
import rules from "../build";
describe("Salaire net", () => {
test("salaire brut par défaut", () => {
const engine = new Engine(rules);
const result = engine.evaluate("salaire net");
expect(result.nodeValue).toBe(1957.5);
expect(serializeEvaluation(result)).toBe("1957.5€/mois");
});
test.each([
[1957.5, 2500],
[2740.5, 3500],
[0, 0],
])("%f €/mois, avec salaire brut = %f €/mois", (salaireNet, salaireBrut) => {
const engine = new Engine(rules);
engine.setSituation({ "salaire brut": salaireBrut });
const result = engine.evaluate("salaire net");
expect(result.nodeValue).toBe(salaireNet);
expect(serializeUnit(result.unit)).toBe("€/mois");
});
});
`
7 changes: 3 additions & 4 deletions test/commands/init.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ describe('publicodes init', () => {

const { stdout } = await cli.execCommand('init -y -p yarn')

expect(stdout).toContain('package.json file written')
expect(stdout).toContain('Dependencies installed')
expect(stdout).toContain('Files generated')
expect(stdout).toContain('New to Publicodes?')
Expand All @@ -26,7 +25,8 @@ describe('publicodes init', () => {
expect(fs.existsSync('node_modules')).toBe(true)
expect(fs.existsSync('yarn.lock')).toBe(true)
expect(fs.existsSync('README.md')).toBe(true)
expect(fs.existsSync('src/base.publicodes')).toBe(true)
expect(fs.existsSync('src/salaire.publicodes')).toBe(true)
expect(fs.existsSync('test/salaire.test.ts')).toBe(true)
})
})

Expand All @@ -36,7 +36,6 @@ describe('publicodes init', () => {

const { stdout } = await cli.execCommand('init -y --no-install -p yarn')

expect(stdout).toContain('package.json file written')
expect(stdout).toContain('Files generated')
expect(stdout).toContain('New to Publicodes?')

Expand All @@ -48,7 +47,7 @@ describe('publicodes init', () => {
expect(fs.existsSync('node_modules')).toBe(false)
expect(fs.existsSync('yarn.lock')).toBe(false)
expect(fs.existsSync('README.md')).toBe(true)
expect(fs.existsSync('src/base.publicodes')).toBe(true)
expect(fs.existsSync('src/salaire.publicodes')).toBe(true)
})
})
})
Expand Down
Loading

0 comments on commit bb364df

Please sign in to comment.