Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • leximpact/simulateur-socio-fiscal/leximpact-socio-fiscal-ui
  • dsmadja/leximpact-socio-fiscal-ui
2 results
Show changes
Commits on Source (21)
......@@ -233,11 +233,26 @@ $: console.log(parameterSmicMensuel, smicValue)
### Générer les YAML de test OpenFisca à partir des cas-type LexImpact
```bash
npx tsx src/scripts/generate_openfisca_tests_yaml.ts -y 2025 ../../openfisca-france/tests/leximpact/
npx tsx src/scripts/generate_openfisca_tests_yaml.ts \
-y 2024 \
--openfisca_venv /path/to/openfisca-france/.venv \
--openfisca_json_model_path ../openfisca-json-model \
--leximpact_openfisca_json_path ../leximpact-socio-fiscal-openfisca-json \
../../openfisca-france/tests/leximpact/2024/
```
- Le paramètre `year` générera les variables d'output pour l'année `year`
- L'argument par défaut `outdir` est le chemin où l'on veut exporter les YAML générés. Ceux-ci ont vocation à être pushés dans openfisca_france/tests/leximpact
Vous aurez besoin :
- d'une install fonctionnelle d'`openfisca-france`
- de clones locaux à jour des dépôts `openfisca-json-model` et `leximpact-socio-fiscal-openfisca-json`
Paramètres attendus :
- l'argument `year` (-y) générera les cas-types avec la valeur du SMIC au 01-01-`year`, les variables d'output pour l'année `year`, et des valeurs d'input pour les années `year`, `year`-1 et `year`-2 selon les données du cas-type.
- l'argument `openfisca_venv` (-e) doit correspondre au chemin d'un venv dans lequel openfisca-france est installé. Il doit être possible d'y exécuter `openfisca test`.
- l'argument `openfisca_json_model_path` doit contenir le chemin vers un clone à jour du dépôt "openfisca-json-model"
- l'argument `leximpact_openfisca_json_path` doit contenir le chemin vers un clone à jour du dépôt "leximpact-socio-fiscal-openfisca-json"
- L'argument par défaut `outdir` est le chemin où l'on veut exporter les YAML générés. Le répertoire fourni sera créé s'il n'existe pas.
Les YAML générés contiennent, pour chaque cas-type, toutes les variables d'entrées du cas-type dans le fichier `test_cases.json`.
Les valeurs sont mensualisées pour toutes les variables mensuelles.
......@@ -245,3 +260,7 @@ Les valeurs sont mensualisées pour toutes les variables mensuelles.
La section `output:` du YAML contient toutes les variables calculées, présentes à la fois dans les décompositions LexImpact **et** dans les variables du country-package Openfisca-France.
Les valeurs des variables de type Enum, sont remplacées par leur index dans les valeurs possibles de l'Enum.
Les valeurs générées en sortie sont celles que retourne l'API d'openFisca-France **corrigées par les valeurs que retournent `openfisca test`** dans le cas où elles diffèrent.
Toutes les valeurs sont arrondies au centime, sauf la valeur d'entrée du SMIC, arrondie à l'euro.
......@@ -81,7 +81,7 @@ async function commitAndPushWithUpdatedVersions(
await $`git add .`
if ((await $`git diff --quiet --staged`.exitCode) !== 0) {
await $`git commit -m ${nextVersion}`
await $`git push --set-upstream`
await $`git push --set-upstream origin master`
}
await $`git tag -a ${nextVersion} -m ${nextVersion}`
await $`git push --set-upstream --tags`
......
{
"name": "leximpact-socio-fiscal-ui",
"version": "0.0.1024",
"version": "0.0.1039",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "leximpact-socio-fiscal-ui",
"version": "0.0.1024",
"version": "0.0.1039",
"devDependencies": {
"@auditors/core": "^0.7.0",
"@eslint/compat": "^1.2.4",
"@fontsource/lato": "^5.0.5",
"@fontsource/lora": "^5.0.5",
"@leximpact/socio-fiscal-openfisca-json": "^0.0.333",
"@leximpact/socio-fiscal-openfisca-json": "^0.0.336",
"@openfisca/json-model": "^3.1.0",
"@playwright/test": "^1.28.1",
"@popperjs/core": "^2.11.6",
......@@ -1074,13 +1074,13 @@
}
},
"node_modules/@leximpact/socio-fiscal-openfisca-json": {
"version": "0.0.333",
"resolved": "https://registry.npmjs.org/@leximpact/socio-fiscal-openfisca-json/-/socio-fiscal-openfisca-json-0.0.333.tgz",
"integrity": "sha512-4Dv3wsGba3PVovrQi1EtGXAdVMakofXZk5u0WIHc4rNrToi29auza6D1wYvKoMOJBl7X/kP/3otOrcODxuck3A==",
"version": "0.0.336",
"resolved": "https://registry.npmjs.org/@leximpact/socio-fiscal-openfisca-json/-/socio-fiscal-openfisca-json-0.0.336.tgz",
"integrity": "sha512-L7gGTI5OMFUbnLDbMk1xpyzyMTbGGyfqrCMHpe5NQmsnJyvmODKljTJP4WS2ZRVDsbX+z5wq4Hcm0oZ2WrLK3w==",
"dev": true,
"license": "AGPL-3.0-or-later",
"peerDependencies": {
"@openfisca/json-model": "^3.2.1"
"@openfisca/json-model": "^3.3.0"
},
"peerDependenciesMeta": {
"@openfisca/json-model": {
......@@ -1127,9 +1127,9 @@
}
},
"node_modules/@openfisca/json-model": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/@openfisca/json-model/-/json-model-3.2.1.tgz",
"integrity": "sha512-aLtYIt7TXd32dl+HzYb1nbhbLrpnzVwuEbzlMik/Btv8F3ZapLIvEcFdyHY7sU+obZOYde6LdBjPCSja99VCNA==",
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/@openfisca/json-model/-/json-model-3.3.0.tgz",
"integrity": "sha512-Ip3qLXgy5DozUVd5wpP2qEvD1W6XJVZxtkDcYa4YAcacICMw8PUH67kElJKLjcrRu5IisINpL06lGXC63BMjKg==",
"dev": true,
"license": "AGPL-3.0-or-later",
"dependencies": {
......
{
"name": "leximpact-socio-fiscal-ui",
"version": "0.0.1026",
"version": "0.0.1040",
"type": "module",
"scripts": {
"build": "NODE_OPTIONS=--max_old_space_size=4096 vite build",
......@@ -18,7 +18,7 @@
"@eslint/compat": "^1.2.4",
"@fontsource/lato": "^5.0.5",
"@fontsource/lora": "^5.0.5",
"@leximpact/socio-fiscal-openfisca-json": "^0.0.333",
"@leximpact/socio-fiscal-openfisca-json": "^0.0.336",
"@openfisca/json-model": "^3.1.0",
"@playwright/test": "^1.28.1",
"@popperjs/core": "^2.11.6",
......
......@@ -752,7 +752,7 @@
variableCustomizations[key] =
variableCustomizations[
getVariableCustomizationName(
variable.parent,
variable.name,
row.calculationName,
)
] ??
......@@ -843,7 +843,7 @@
variableCustomizations[key] =
variableCustomizations[
getVariableCustomizationName(
variable.parent,
variable.name,
row.calculationName,
)
] ??
......@@ -1006,6 +1006,7 @@
>
{#each getCustomizations(groupName, row.calculationName) as customization}
<button
aria-label="personnaliser l'apparence"
class="h-10 w-10 rounded border-2 border-neutral-300 hover:border-blue-500"
onclick={() => {
variableCustomizations[
......@@ -1061,6 +1062,7 @@
initialPlacement="top"
>
<button
aria-labelledby="hide-show-tooltip-{variable.name}"
class="group h-full pl-0.5 pr-1"
onclick={() => {
variableCustomizations[
......@@ -1095,7 +1097,7 @@
</button>
{#snippet tooltip()}
<div
id="hide-show-tooltip"
id="hide-show-tooltip-{variable.name}"
class="overflow-hidden rounded-lg bg-black px-2 py-1 text-sm text-white"
title={isSelected ? "Cacher" : "Montrer"}
>
......
......@@ -79,10 +79,10 @@ export const revaluationName: string | undefined =
const today = new Date()
export const year =
today.getFullYear() + (today.getMonth() > 2 /* => After March */ ? 1 : 0)
today.getFullYear() + (today.getMonth() > 3 /* => After April */ ? 1 : 0)
export const yearPLF =
today.getFullYear() +
(revaluationName !== undefined && today.getMonth() > 2 /* => After March */
(revaluationName !== undefined && today.getMonth() > 3 /* => After April */
? 1
: 0)
export const date = `${year}-01-01`
......
......@@ -303,9 +303,13 @@ export function setSituationVariableValue(
) {
return false
}
valueByPeriod[year - 2] = value
valueByPeriod[year - 1] = value
valueByPeriod[year] = value
if (variable.name === "date_naissance") {
valueByPeriod[year] = value
} else {
valueByPeriod[year - 2] = value
valueByPeriod[year - 1] = value
valueByPeriod[year] = value
}
return true
}
......
......@@ -3,7 +3,6 @@ import commandLineArgs from "command-line-args"
import fs from "fs-extra"
import path from "path"
import YAML from "js-yaml"
import testCasesCoreUnknown from "@leximpact/socio-fiscal-openfisca-json/test_cases.json"
import {
summaryCalculatedVariablesName,
otherCalculatedVariablesName,
......@@ -12,6 +11,7 @@ import {
import { nonVirtualVariablesName } from "$lib/decompositions"
import type { EntityByKey, Entity, GroupEntity } from "@openfisca/json-model"
import { entityByKey } from "$lib/entities"
import { spawn } from "child_process"
type JsonObject = { [key: string]: any }
......@@ -34,6 +34,28 @@ const optionsDefinitions = [
name: "year",
type: Number,
},
{
alias: "e",
help: "Path to a venv with openfisca-france installed. You must be able to launch `openfisca test` within it.",
name: "openfisca_venv",
type: String,
},
{
help: "Path to openfisca-json-model. Be careful to clone the repo and git pull before.",
name: "openfisca_json_model_path",
type: String,
},
{
help: "Path to leximpact-socio_fiscal-openfisca-json. Be careful to clone the repo and git pull before.",
name: "leximpact_openfisca_json_path",
type: String,
},
{
alias: "t",
help: "Single Test-Case to run",
name: "testcase",
type: String,
},
{
defaultOption: true,
help: "Directory to write OpenFisca test YAML files",
......@@ -43,6 +65,64 @@ const optionsDefinitions = [
]
const options = commandLineArgs(optionsDefinitions)
function getSmicForYear(
smicValues: Record<string, number>,
year: number,
): number | null {
const targetDate = `${year}-01-01`
const validDates = Object.keys(smicValues)
.filter((date) => date <= targetDate)
.sort()
if (validDates.length === 0) {
return null
}
const lastValidDate = validDates[validDates.length - 1]
return smicValues[lastValidDate]
}
async function runMergeTestCases(
openFiscaJsonModelPath: string,
leximpactOpenFiscaJsonPath: string,
smicValue: number,
year: number,
): Promise<any> {
return new Promise<void>((resolve, reject) => {
const scriptPath = path.join(
openFiscaJsonModelPath,
"packages",
"tools",
"src",
"scripts",
"merge_test_cases.ts",
)
const args = [
scriptPath,
leximpactOpenFiscaJsonPath,
"--smic=" + smicValue.toString(),
"--year",
year.toString(),
]
const processMergeTestCases = spawn("npx", ["tsx", ...args], {
stdio: "inherit",
shell: true,
})
processMergeTestCases.on("close", (code) => {
if (code !== 0) {
reject(
"Unable to generate new test_cases.json. Call to script merge_test_cases.ts failed with code : " +
code,
)
} else {
resolve()
}
})
})
}
async function fetchWithRetries(
url: string,
options: RequestInit,
......@@ -151,13 +231,13 @@ function replaceValuesFromReplacedKeys(
return newObj
}
function removeZeroValues(obj: any): any {
function removeZeroValuesAndExcludedVariables(obj: any): any {
if (typeof obj !== "object" || obj === null) {
return obj
}
if (Array.isArray(obj)) {
return obj.map(removeZeroValues)
return obj.map(removeZeroValuesAndExcludedVariables)
}
const newObj: any = {}
......@@ -167,14 +247,19 @@ function removeZeroValues(obj: any): any {
const value = obj[key]
if (typeof value === "object" && value !== null) {
if (Object.values(value).every((v) => v === 0 || v === null)) {
if (
Object.values(value).every((v) => v === 0 || v === null) ||
key === "prelevements_sociaux_menage"
) {
continue
}
const cleanedValue = removeZeroValues(value)
const cleanedValue = removeZeroValuesAndExcludedVariables(value)
if (Object.keys(cleanedValue).length > 0) {
newObj[key] = cleanedValue
}
} else if (typeof value === "number") {
newObj[key] = Math.round(value * 100) / 100
} else {
newObj[key] = value
}
......@@ -192,7 +277,7 @@ function cleanSimulatedJson(jsonData: any): any {
jsonData[section] = Object.fromEntries(
Object.entries(jsonData[section]).map(([key, value]) => [
key,
removeZeroValues(value),
removeZeroValuesAndExcludedVariables(value),
]),
)
}
......@@ -202,14 +287,22 @@ function cleanSimulatedJson(jsonData: any): any {
}
function splitVariablesByCategory(
section: string,
entityKey: string,
entityData: JsonObject,
outputVariablesList: string[],
initialTestCase: Situation,
) {
const input: JsonObject = {}
const output: JsonObject = {}
for (const [variable, value] of Object.entries(entityData)) {
if (outputVariablesList.includes(variable)) {
const replacedKeys: { [key: string]: string } = {}
initialTestCase = removeSpacesFromKeys(initialTestCase, replacedKeys)
if (
initialTestCase[section][entityKey] !== undefined &&
initialTestCase[section][entityKey][variable] === undefined
) {
if (
variableSummaryByName[variable].value_type === "Enum" &&
variableSummaryByName[variable].possible_values !== undefined
......@@ -233,16 +326,19 @@ function splitVariablesByCategory(
}
function processSection(
section: string,
sectionData: JsonObject,
outputVariablesList: string[],
initialTestCase: Situation,
) {
const inputSection: JsonObject = {}
const outputSection: JsonObject = {}
for (const [entityKey, entityData] of Object.entries(sectionData)) {
const { input, output } = splitVariablesByCategory(
section,
entityKey,
entityData,
outputVariablesList,
initialTestCase,
)
if (Object.keys(input).length > 0) {
......@@ -260,19 +356,21 @@ function buildYamlOutput(
name: string,
year: string,
simulatedTestCase: JsonObject,
outputVariablesList: string[],
testCase: Situation,
) {
const sections = ["familles", "foyers_fiscaux", "menages", "individus"]
const jsonForYamlOutput: {
name: string
period: string
max_spiral_loops: number
absolute_error_margin: number
input: JsonObject
output: JsonObject
} = {
name: name,
period: year,
max_spiral_loops: 4,
absolute_error_margin: 1,
input: {},
output: {},
}
......@@ -280,8 +378,9 @@ function buildYamlOutput(
for (const section of sections) {
if (simulatedTestCase[section]) {
const { input, output } = processSection(
section,
simulatedTestCase[section],
outputVariablesList,
testCase,
)
if (Object.keys(input).length > 0) {
......@@ -365,14 +464,128 @@ function isKeyOrPlural(str: string): boolean {
return false
}
function replaceVariableValue(
jsonData: any,
variableName: string,
currentValue: any,
newValue: any,
period: string,
): any {
const entities = ["familles", "foyers_fiscaux", "menages", "individus"]
for (const entity of entities) {
if (!jsonData.output[entity]) continue
for (const entityKey in jsonData.output[entity]) {
const entityData = jsonData.output[entity][entityKey]
if (entityData.hasOwnProperty(variableName)) {
const variableData = entityData[variableName]
if (typeof variableData === "object") {
if (
variableData.hasOwnProperty(period) &&
Math.trunc(Number(variableData[period])) ===
Math.trunc(Number(currentValue))
) {
console.info(
`Replacing ${variableName} (${period}): ${variableData[period]}${newValue}`,
)
variableData[period] = Math.round(Number(newValue) * 100) / 100
} else {
console.log(
"Warning : rounded value",
Math.trunc(Number(currentValue)),
"not found for",
variableName,
"\nThe cause can be multiple instance of the same variable for different entities... or bug",
)
}
}
}
}
}
return
}
async function runOpenFiscaTest(
openFiscaVenvPath: string,
yamlFilePath: string,
): Promise<any> {
return new Promise<void>((resolve, reject) => {
const openfiscaExecutable = path.join(openFiscaVenvPath, "bin", "openfisca")
const args = ["test", "--country-package", "openfisca_france", yamlFilePath]
const env = {
...process.env,
VIRTUAL_ENV: openFiscaVenvPath,
PATH: `${path.join(openFiscaVenvPath, "bin")}:${process.env.PATH}`,
PYTHONPATH: path.join(
openFiscaVenvPath,
"lib",
"python3.12",
"site-packages",
),
}
let testOutput = ""
const pythonProcess = spawn(openfiscaExecutable, args, {
stdio: ["inherit", "pipe", "pipe"],
env,
})
pythonProcess.stdout.on("data", (data) => {
testOutput += data.toString()
})
pythonProcess.stderr.on("data", (data) => {
console.error(`stderr: ${data}`)
reject(data)
})
pythonProcess.on("close", (code) => {
let returned = undefined as any
if (code !== 0) {
returned = { passed: false }
const testOutputLines = testOutput.split("\n")
const regex =
/(?<variable>[a-zA-Z0-9_]+)@(?<period>\d{4}(?:-\d{2})?):\s+(?<calculated>-?\d+(?:\.\d+)?)\s+differs\s+from\s+(?<expected>-?\d+(?:\.\d+)?)/
testOutputLines.forEach((line) => {
const match = line.match(regex)
if (match && match.groups) {
returned = { ...returned, ...match.groups }
}
})
if (returned.variable !== undefined) {
resolve(returned)
} else
reject(
"Erreur de récupération des valeurs lors d'un appel à openfisca test : " +
testOutput,
)
} else {
returned = { passed: true }
resolve(returned)
}
})
})
}
async function main() {
console.info("Initializing...")
const personsEntityKey = Object.entries(entityByKey)
.filter(([, entity]) => entity.is_person)
.map(([key]) => key)[0]
const year = options.year
const outdir = options.outdir
const singleTestCase = options.testcase
const openFiscaVenvPath = options.openfisca_venv
const openFiscaJsonModelPath = options.openfisca_json_model_path
const leximpactOpenFiscaJsonPath = options.leximpact_openfisca_json_path
if (!fs.existsSync(outdir)) {
fs.mkdirSync(outdir, { recursive: true })
}
const variablesFromDecompositions = [
...summaryCalculatedVariablesName,
......@@ -397,17 +610,64 @@ async function main() {
const openFiscaVariablesList = await openFiscaVariablesListApiCall.json()
const smicValuesApiCall = await fetch(
"https://api.fr.openfisca.org/latest/parameter/marche_travail/salaire_minimum/smic/smic_b_mensuel",
{
method: "GET",
},
)
if (!smicValuesApiCall.ok) {
console.error(
`Error ${smicValuesApiCall.status} while calling Openfisca France API`,
)
return
}
const smicValues = (await smicValuesApiCall.json()).values
const monthlySmicValueForYear = getSmicForYear(smicValues, year)
if (monthlySmicValueForYear === null) {
throw new Error("Unable to retrieve SMIC value for year", year)
}
const smicValueForYear = Math.round(monthlySmicValueForYear * 12)
console.info(
"SMIC value retrieved from openFisca API. Will be using",
smicValueForYear,
)
console.info("Generating test cases...")
try {
await runMergeTestCases(
openFiscaJsonModelPath,
leximpactOpenFiscaJsonPath,
smicValueForYear,
year,
)
} catch (error) {
console.error("Error : ", error)
}
const testCasesCoreData = await fs.readFile(
path.join(leximpactOpenFiscaJsonPath, "test_cases.json"),
"utf-8",
)
const testCasesCore = JSON.parse(testCasesCoreData)
const testCaseCount = testCasesCore.length
const variablesToCalculate = variablesFromDecompositions.filter(
(variable: string) => openFiscaVariablesList.hasOwnProperty(variable),
)
const testCasesCore = testCasesCoreUnknown as unknown as Situation[]
let processedCounter = 0
for (const testCase of testCasesCore) {
if (singleTestCase !== undefined && testCase.id !== singleTestCase) {
continue
}
console.info("Processing ", testCase.id)
processedCounter++
let testCaseContainsMissingVariable = false
let jsonForApiCall: JsonObject = {}
const outputVariablesList: Array<string> = []
jsonForApiCall.input = {}
......@@ -432,7 +692,7 @@ async function main() {
!openFiscaVariablesList.hasOwnProperty(variable) &&
!isKeyOrPlural(variable)
) {
console.warn(
console.info(
"variable",
variable,
"does not exists in openFisca-France.",
......@@ -445,7 +705,7 @@ async function main() {
}
if (testCaseContainsMissingVariable) {
console.warn("Ignoring test case", testCase.id)
console.info("Ignoring test case", testCase.id)
continue
}
......@@ -463,7 +723,6 @@ async function main() {
getEntityKeyFromKeyPlural(section, entityByKey) &&
jsonForApiCall.input[section][entityKey][variable] === undefined
) {
outputVariablesList.push(variable)
switch (variableSummaryByName[variable].definition_period) {
case "year": {
jsonForApiCall.input[section][entityKey][variable] = {
......@@ -497,8 +756,13 @@ async function main() {
}
jsonForApiCall = replaceValuesFromReplacedKeys(jsonForApiCall, replacedKeys)
// await fs.writeFile(
// path.join(".", testCase.id + ".json"),
// JSON.stringify(jsonForApiCall.input, null, 2),
// )
console.info("Launching simulation on OpenFisca France API...")
const openFiscaApiUrl = "https://api.fr.openfisca.org/latest/calculate"
// const openFiscaApiUrl = "http://localhost:5000/calculate"
const openFiscaApiOptions: RequestInit = {
body: JSON.stringify(jsonForApiCall.input),
headers: {
......@@ -533,18 +797,93 @@ async function main() {
testCase.id!,
year,
simulatedTestCase,
outputVariablesList,
testCase,
)
const yamlOutput = YAML.dump(jsonForYamlOutput, {
noCompatMode: true,
noRefs: true,
})
let ok = false
while (!ok) {
const yamlOutput = YAML.dump(jsonForYamlOutput, {
noCompatMode: true,
noRefs: true,
})
const cleanedYaml = yamlOutput.replace(/'(\d{4})':/g, "$1:")
await fs.writeFile(path.join(outdir, testCase.id + ".yml"), cleanedYaml)
try {
const openFiscaTestResult = await runOpenFiscaTest(
openFiscaVenvPath,
path.join(outdir, testCase.id + ".yml"),
)
if (!openFiscaTestResult.passed) {
if (
openFiscaTestResult.variable === undefined ||
openFiscaTestResult.expected === undefined ||
openFiscaTestResult.calculated === undefined ||
openFiscaTestResult.period === undefined
) {
throw new Error("Error retrieving values to update")
}
replaceVariableValue(
jsonForYamlOutput,
openFiscaTestResult.variable,
openFiscaTestResult.expected,
openFiscaTestResult.calculated,
openFiscaTestResult.period,
)
} else {
ok = true
console.info(
"YAML file written :",
path.join(outdir, testCase.id + ".yml"),
)
}
} catch (error) {
console.error(error)
}
}
console.info("Adjusting max_spiral_loop")
while (ok && jsonForYamlOutput.max_spiral_loops > 1) {
jsonForYamlOutput.max_spiral_loops =
jsonForYamlOutput.max_spiral_loops - 1
const yamlOutput = YAML.dump(jsonForYamlOutput, {
noCompatMode: true,
noRefs: true,
})
const cleanedYaml = yamlOutput.replace(/'(\d{4})':/g, "$1:")
await fs.writeFile(path.join(outdir, testCase.id + ".yml"), cleanedYaml)
try {
const openFiscaTestResult = await runOpenFiscaTest(
openFiscaVenvPath,
path.join(outdir, testCase.id + ".yml"),
)
if (!openFiscaTestResult.passed) {
ok = false
jsonForYamlOutput.max_spiral_loops =
jsonForYamlOutput.max_spiral_loops + 1
const yamlOutput = YAML.dump(jsonForYamlOutput, {
noCompatMode: true,
noRefs: true,
})
const cleanedYaml = yamlOutput.replace(/'(\d{4})':/g, "$1:")
await fs.writeFile(
path.join(outdir, testCase.id + ".yml"),
cleanedYaml,
)
console.info("YAML file written :", outdir, testCase.id + ".yml")
} else {
ok = true
}
} catch (error) {
console.error(error)
}
}
// Remove quotes around year keys
const cleanedYaml = yamlOutput.replace(/'(\d{4})':/g, "$1:")
await fs.writeFile(path.join(outdir, testCase.id + ".yml"), cleanedYaml)
console.info("YAML file written :", outdir, testCase.id + ".yml")
console.info("Processed test-case", processedCounter, "over", testCaseCount)
}
}
......