@tricoteuses/senat 3.2.0 → 3.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +32 -32
- package/README.md +326 -326
- package/lib/src/config.d.ts +43 -0
- package/lib/src/config.js +37 -0
- package/lib/src/conversion_textes.d.ts +11 -0
- package/lib/src/conversion_textes.js +320 -0
- package/lib/src/databases_postgres.d.ts +4 -0
- package/lib/src/databases_postgres.js +23 -0
- package/lib/src/datasets.d.ts +38 -0
- package/lib/src/datasets.js +247 -0
- package/lib/src/git.d.ts +27 -0
- package/lib/src/git.js +251 -0
- package/lib/src/index.d.ts +1 -1
- package/lib/src/loaders.d.ts +52 -0
- package/lib/src/loaders.js +260 -0
- package/lib/src/model/agenda.d.ts +6 -0
- package/lib/src/model/agenda.js +148 -0
- package/lib/src/model/ameli.d.ts +67 -0
- package/lib/src/model/ameli.js +150 -0
- package/lib/src/model/commission.d.ts +19 -0
- package/lib/src/model/commission.js +269 -0
- package/lib/src/model/debats.d.ts +39 -0
- package/lib/src/model/debats.js +112 -0
- package/lib/src/model/documents.d.ts +32 -0
- package/lib/src/model/documents.js +182 -0
- package/lib/src/model/dosleg.d.ts +144 -0
- package/lib/src/model/dosleg.js +468 -0
- package/lib/src/model/index.d.ts +7 -0
- package/lib/src/model/index.js +7 -0
- package/lib/src/model/questions.d.ts +54 -0
- package/lib/src/model/questions.js +91 -0
- package/lib/src/model/scrutins.d.ts +48 -0
- package/lib/src/model/scrutins.js +121 -0
- package/lib/src/model/seance.d.ts +3 -0
- package/lib/src/model/seance.js +267 -0
- package/lib/src/model/sens.d.ts +112 -0
- package/lib/src/model/sens.js +385 -0
- package/lib/src/model/util.d.ts +1 -0
- package/lib/src/model/util.js +15 -0
- package/lib/src/parsers/plf/amendement.d.ts +3 -0
- package/lib/src/parsers/plf/amendement.js +152 -0
- package/lib/src/raw_types/ameli.d.ts +1762 -0
- package/lib/src/raw_types/ameli.js +1074 -0
- package/lib/src/raw_types/debats.d.ts +380 -0
- package/lib/src/raw_types/debats.js +266 -0
- package/lib/src/raw_types/dosleg.d.ts +2954 -0
- package/lib/src/raw_types/dosleg.js +2005 -0
- package/lib/src/raw_types/questions.d.ts +699 -0
- package/lib/src/raw_types/questions.js +493 -0
- package/lib/src/raw_types/sens.d.ts +7843 -0
- package/lib/src/raw_types/sens.js +4691 -0
- package/lib/src/raw_types_schemats/ameli.d.ts +541 -0
- package/lib/src/raw_types_schemats/ameli.js +2 -0
- package/lib/src/raw_types_schemats/debats.d.ts +127 -0
- package/lib/src/raw_types_schemats/debats.js +2 -0
- package/lib/src/raw_types_schemats/dosleg.d.ts +977 -0
- package/lib/src/raw_types_schemats/dosleg.js +2 -0
- package/lib/src/raw_types_schemats/questions.d.ts +237 -0
- package/lib/src/raw_types_schemats/questions.js +2 -0
- package/lib/src/raw_types_schemats/sens.d.ts +2709 -0
- package/lib/src/raw_types_schemats/sens.js +2 -0
- package/lib/src/rich_types/ameli.d.ts +12 -0
- package/lib/src/scripts/debug_dosleg_query.d.ts +6 -0
- package/lib/src/scripts/debug_dosleg_query.js +50 -0
- package/lib/src/scripts/retrieve_collaborateurs.js +6 -3
- package/lib/src/scripts/retrieve_open_data.js +2 -1
- package/lib/src/scripts/shared/incremental_import_sql.js +866 -866
- package/lib/src/scripts/shared/schema_version.js +85 -84
- package/lib/src/scripts/shared/staging_metadata_sql.js +214 -214
- package/lib/src/scripts/validate_prefixed_tables.js +12 -12
- package/lib/src/server/ameli.js +18 -11
- package/lib/src/server/conversion_textes.js +106 -106
- package/lib/src/server/datasets.js +1 -0
- package/lib/src/server/debats.js +10 -10
- package/lib/src/server/documents.js +22 -22
- package/lib/src/server/dosleg.js +33 -33
- package/lib/src/server/questions.js +10 -10
- package/lib/src/server/scrutins.js +3 -3
- package/lib/src/server/sens.js +19 -19
- package/lib/src/types/agenda.d.ts +45 -0
- package/lib/src/types/ameli.d.ts +5 -0
- package/lib/src/types/ameli.js +1 -0
- package/lib/src/types/compte_rendu.d.ts +83 -0
- package/lib/src/types/compte_rendu.js +1 -0
- package/lib/src/types/debats.d.ts +2 -0
- package/lib/src/types/debats.js +1 -0
- package/lib/src/types/dosleg.d.ts +70 -0
- package/lib/src/types/dosleg.js +1 -0
- package/lib/src/types/questions.d.ts +2 -0
- package/lib/src/types/questions.js +1 -0
- package/lib/src/types/sens.d.ts +8 -0
- package/lib/src/types/sens.js +1 -0
- package/lib/src/types/sessions.d.ts +6 -0
- package/lib/src/types/sessions.js +19 -0
- package/lib/src/types/texte.d.ts +72 -0
- package/lib/src/types/texte.js +15 -0
- package/lib/src/validators/config.d.ts +9 -0
- package/lib/src/validators/config.js +10 -0
- package/lib/src/videos/match.js +4 -4
- package/lib/tests/parsers/plf/amendement.test.d.ts +1 -0
- package/lib/tests/parsers/plf/amendement.test.js +153 -0
- package/lib/tests/parsers/plf/credits.test.js +72 -72
- package/lib/tests/parsers/plf/test_utils.js +13 -13
- package/lib/tests/parsers/texte.test_utils.js +22 -22
- package/lib/tests/videoMatching.test.js +1 -1
- package/package.json +123 -123
- package/lib/src/scripts/extract_plf_fixtures.d.ts +0 -1
- package/lib/src/scripts/extract_plf_fixtures.js +0 -101
- package/lib/tests/plf/credits.test.js +0 -458
- package/lib/tests/plf/test_utils.d.ts +0 -20
- package/lib/tests/plf/test_utils.js +0 -85
- /package/lib/{tests/plf/credits.test.d.ts → src/types/agenda.js} +0 -0
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import fs from "fs-extra";
|
|
2
|
-
import { JSDOM } from "jsdom";
|
|
3
|
-
import path from "path";
|
|
4
|
-
import { commonOptions } from "./shared/cli_helpers.js";
|
|
5
|
-
const optionsDefinitions = [
|
|
6
|
-
{ name: "src", type: String },
|
|
7
|
-
{
|
|
8
|
-
help: [
|
|
9
|
-
"extract the 'etat' articles of a real Sénat AKN XML into a minimal fixture",
|
|
10
|
-
"named <name>.xml under tests/fixtures/plf/",
|
|
11
|
-
].join(" "),
|
|
12
|
-
name: "name",
|
|
13
|
-
type: String,
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
help: "keep only the parsed budget states (B, C, D); drop the large A/G tables",
|
|
17
|
-
name: "only-parsed",
|
|
18
|
-
type: Boolean,
|
|
19
|
-
},
|
|
20
|
-
...commonOptions,
|
|
21
|
-
];
|
|
22
|
-
const KEEP_DATA_ROWS = 6;
|
|
23
|
-
function trimTable(table) {
|
|
24
|
-
const rows = [...table.querySelectorAll("tr")];
|
|
25
|
-
// Les 2 premières lignes sont les en-têtes réels : « (En euros) » puis « Mission / Programme ».
|
|
26
|
-
const headerCount = Math.min(2, rows.length);
|
|
27
|
-
const dataRows = rows.slice(headerCount);
|
|
28
|
-
const dataStartIndex = rows.length - dataRows.length;
|
|
29
|
-
const totalRows = dataRows.filter((row) => {
|
|
30
|
-
const firstTd = row.querySelector("td");
|
|
31
|
-
return /^total/i.test(firstTd?.textContent?.trim() ?? "");
|
|
32
|
-
});
|
|
33
|
-
const totalIndexes = new Set(totalRows.map((row) => rows.indexOf(row)));
|
|
34
|
-
let kept = 0;
|
|
35
|
-
for (const row of rows) {
|
|
36
|
-
const index = rows.indexOf(row);
|
|
37
|
-
if (index < dataStartIndex)
|
|
38
|
-
continue; // en-têtes conservés
|
|
39
|
-
if (totalIndexes.has(index))
|
|
40
|
-
continue; // lignes Total conservées
|
|
41
|
-
if (kept < KEEP_DATA_ROWS) {
|
|
42
|
-
kept++;
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
row.parentNode?.removeChild(row);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
export async function extractEtats(srcPath) {
|
|
49
|
-
const xml = await fs.readFile(srcPath, "utf-8");
|
|
50
|
-
const { document } = new JSDOM(xml, { contentType: "text/xml" }).window;
|
|
51
|
-
const body = document.querySelector("body");
|
|
52
|
-
if (!body)
|
|
53
|
-
throw new Error(`No <body> found in ${srcPath}`);
|
|
54
|
-
return [...body.querySelectorAll('article[class="etat"]')];
|
|
55
|
-
}
|
|
56
|
-
async function main() {
|
|
57
|
-
const commandLineArgs = (await import("command-line-args")).default;
|
|
58
|
-
const options = commandLineArgs(optionsDefinitions);
|
|
59
|
-
const srcPath = options.src;
|
|
60
|
-
const fixtureName = options.name;
|
|
61
|
-
if (!srcPath)
|
|
62
|
-
throw new Error("Missing <src> path to a Sénat AKN XML");
|
|
63
|
-
if (!fixtureName)
|
|
64
|
-
throw new Error("Missing --name");
|
|
65
|
-
const etatArticles = await extractEtats(srcPath);
|
|
66
|
-
const kept = options["only-parsed"]
|
|
67
|
-
? etatArticles.filter((article) => {
|
|
68
|
-
const num = article.querySelector(":scope > num")?.textContent?.trim();
|
|
69
|
-
return num === "État B" || num === "État C" || num === "État D";
|
|
70
|
-
})
|
|
71
|
-
: etatArticles;
|
|
72
|
-
if (kept.length === 0)
|
|
73
|
-
throw new Error(`No B/C/D etat articles found in ${srcPath}`);
|
|
74
|
-
for (const article of kept) {
|
|
75
|
-
for (const table of article.querySelectorAll("table.alinea-table.etat-tableau")) {
|
|
76
|
-
trimTable(table);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
const outDir = path.join("tests", "fixtures", "plf");
|
|
80
|
-
await fs.ensureDir(outDir);
|
|
81
|
-
const outPath = path.join(outDir, `${fixtureName}.xml`);
|
|
82
|
-
const fixture = `<?xml version="1.0" encoding="UTF-8"?>
|
|
83
|
-
<akomaNtoso xmlns="http://docs.oasis-open.org/legaldocml/ns/akn/3.0" xmlns:data="http://data.parlement.fr/v1">
|
|
84
|
-
<bill contains="singleVersion" name="plf">
|
|
85
|
-
<body>${kept.map((el) => el.outerHTML).join("\n")}</body>
|
|
86
|
-
</bill>
|
|
87
|
-
</akomaNtoso>
|
|
88
|
-
`;
|
|
89
|
-
await fs.writeFile(outPath, fixture);
|
|
90
|
-
if (!options.silent) {
|
|
91
|
-
const sizeKb = (Buffer.byteLength(fixture) / 1024).toFixed(1);
|
|
92
|
-
console.log(`Wrote ${outPath} (${kept.length} etat articles, ${sizeKb} KB)`);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
if (process.argv[1].endsWith("extract_plf_fixtures.ts")) {
|
|
96
|
-
main()
|
|
97
|
-
.catch((error) => {
|
|
98
|
-
console.error(error);
|
|
99
|
-
process.exit(1);
|
|
100
|
-
});
|
|
101
|
-
}
|
|
@@ -1,458 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { parsePlfCredits } from "../../src/parsers/plf/index.js";
|
|
3
|
-
import { buildArticle, etatTable3Col, etatTable5Col, lgncredRow, lgncredRow5, missionRow, missionRow5, parseXml, programmeRow, programmeRow5, styledDontTitreRow, styledMissionRow, styledMissionRow5, styledProgrammeRow, styledProgrammeRow5, styledTotalRow, styledTotalRow5, totalRow, totalRow5, xmlDoc, } from "./test_utils.js";
|
|
4
|
-
// =======================================================================
|
|
5
|
-
// Tests
|
|
6
|
-
// =======================================================================
|
|
7
|
-
describe("parsePlfCredits", () => {
|
|
8
|
-
// ---------------------------------------------------------------------
|
|
9
|
-
// 1. Pas d'article etat
|
|
10
|
-
// ---------------------------------------------------------------------
|
|
11
|
-
it("returns empty array when no etat article is present", () => {
|
|
12
|
-
const doc = parseXml(xmlDoc("<article><num>Article 1er</num></article>"));
|
|
13
|
-
expect(parsePlfCredits(doc)).toEqual([]);
|
|
14
|
-
});
|
|
15
|
-
// ---------------------------------------------------------------------
|
|
16
|
-
// 2. État C (Conforme) → ignoré
|
|
17
|
-
// ---------------------------------------------------------------------
|
|
18
|
-
it("ignores État C when marked (Conforme)", () => {
|
|
19
|
-
const doc = parseXml(xmlDoc(buildArticle("etat_C", "État C", "<alinea eId=\"al_1__etat_C\" data:mention=\"(Conforme)\">" +
|
|
20
|
-
"<content><p><i>(Conforme)</i></p></content></alinea>")));
|
|
21
|
-
expect(parsePlfCredits(doc)).toEqual([]);
|
|
22
|
-
});
|
|
23
|
-
// ---------------------------------------------------------------------
|
|
24
|
-
// 3. État B 3 colonnes — format classé
|
|
25
|
-
// ---------------------------------------------------------------------
|
|
26
|
-
it("parses État B with semantic classes (3 columns)", () => {
|
|
27
|
-
const table = etatTable3Col("B", missionRow("Action extérieure de l'État", "3 449 452 613", "3 454 425 325") +
|
|
28
|
-
programmeRow("Action de la France en Europe et dans le monde", "2 685 599 416", "2 690 168 428") +
|
|
29
|
-
lgncredRow("dont titre 2", "1 385 974 708", "1 385 974 708") +
|
|
30
|
-
programmeRow("Diplomatie culturelle et d'influence", "611 326 220", "611 326 220") +
|
|
31
|
-
missionRow("Administration générale et territoriale de l'État", "4 996 176 839", "5 081 543 463") +
|
|
32
|
-
programmeRow("Administration territoriale de l'État", "2 789 623 725", "2 739 049 891") +
|
|
33
|
-
totalRow("620 249 232 626", "593 890 071 649"));
|
|
34
|
-
const doc = parseXml(xmlDoc(buildArticle("etat_B", "État B", table)));
|
|
35
|
-
const result = parsePlfCredits(doc);
|
|
36
|
-
expect(result).toHaveLength(1);
|
|
37
|
-
expect(result[0].budgetType).toBe("budgetGeneral");
|
|
38
|
-
expect(result[0].libelle).toBe("Budget général");
|
|
39
|
-
expect(result[0].missions).toHaveLength(2);
|
|
40
|
-
// Mission 1
|
|
41
|
-
const m1 = result[0].missions[0];
|
|
42
|
-
expect(m1.libelle).toBe("Action extérieure de l'État");
|
|
43
|
-
expect(m1.autorisationEngagement).toBe(3449452613);
|
|
44
|
-
expect(m1.creditPaiement).toBe(3454425325);
|
|
45
|
-
expect(m1.programmes).toHaveLength(2);
|
|
46
|
-
expect(m1.programmes[0].libelle).toBe("Action de la France en Europe et dans le monde");
|
|
47
|
-
expect(m1.programmes[0].autorisationEngagement).toBe(2685599416);
|
|
48
|
-
expect(m1.programmes[0].creditPaiement).toBe(2690168428);
|
|
49
|
-
expect(m1.programmes[0].lignes).toHaveLength(1);
|
|
50
|
-
expect(m1.programmes[0].lignes[0].libelle).toBe("dont titre 2");
|
|
51
|
-
expect(m1.programmes[0].lignes[0].autorisationEngagement).toBe(1385974708);
|
|
52
|
-
// Mission 2
|
|
53
|
-
const m2 = result[0].missions[1];
|
|
54
|
-
expect(m2.libelle).toBe("Administration générale et territoriale de l'État");
|
|
55
|
-
expect(m2.programmes).toHaveLength(1);
|
|
56
|
-
// Total
|
|
57
|
-
expect(result[0].total).toBeDefined();
|
|
58
|
-
expect(result[0].total.autorisationEngagement).toBe(620249232626);
|
|
59
|
-
expect(result[0].total.creditPaiement).toBe(593890071649);
|
|
60
|
-
});
|
|
61
|
-
// ---------------------------------------------------------------------
|
|
62
|
-
// 4. État B 3 colonnes — format non-classé (style only)
|
|
63
|
-
// ---------------------------------------------------------------------
|
|
64
|
-
it("parses État B with inline styles only (3 columns)", () => {
|
|
65
|
-
const table = etatTable3Col("B", styledMissionRow("Action extérieure de l'État", "3 449 452 613", "3 454 425 325") +
|
|
66
|
-
styledProgrammeRow("Action de la France en Europe et dans le monde", "2 685 599 416", "2 690 168 428") +
|
|
67
|
-
styledDontTitreRow("1 385 974 708", "1 385 974 708") +
|
|
68
|
-
styledMissionRow("Administration générale et territoriale de l'État", "4 996 176 839", "5 081 543 463") +
|
|
69
|
-
styledProgrammeRow("Administration territoriale de l'État", "2 789 623 725", "2 739 049 891") +
|
|
70
|
-
styledTotalRow("620 249 232 626", "593 890 071 649"));
|
|
71
|
-
const doc = parseXml(xmlDoc(buildArticle("etat_B", "État B", table)));
|
|
72
|
-
const result = parsePlfCredits(doc);
|
|
73
|
-
expect(result).toHaveLength(1);
|
|
74
|
-
expect(result[0].budgetType).toBe("budgetGeneral");
|
|
75
|
-
expect(result[0].missions).toHaveLength(2);
|
|
76
|
-
const m1 = result[0].missions[0];
|
|
77
|
-
expect(m1.libelle).toBe("Action extérieure de l'État");
|
|
78
|
-
expect(m1.autorisationEngagement).toBe(3449452613);
|
|
79
|
-
expect(m1.programmes[0].libelle).toBe("Action de la France en Europe et dans le monde");
|
|
80
|
-
expect(m1.programmes[0].lignes[0].libelle).toBe("Dont titre 2");
|
|
81
|
-
expect(result[0].total.autorisationEngagement).toBe(620249232626);
|
|
82
|
-
});
|
|
83
|
-
// ---------------------------------------------------------------------
|
|
84
|
-
// 5. État B 5 colonnes — format non-classé (PLFG)
|
|
85
|
-
// ---------------------------------------------------------------------
|
|
86
|
-
it("parses État B with 5 columns (PLFG style)", () => {
|
|
87
|
-
const table = etatTable5Col("B", styledMissionRow5("Action extérieure de l'État", "", "", "73 072 877", "68 940 645") +
|
|
88
|
-
styledProgrammeRow5("Action de la France en Europe et dans le monde", "", "", "49 000 000", "46 000 000") +
|
|
89
|
-
styledMissionRow5("Administration générale et territoriale de l'État", "", "", "157 618 769", "111 127 278") +
|
|
90
|
-
styledProgrammeRow5("Administration territoriale de l'État", "", "", "4 468 220", "6 036 676") +
|
|
91
|
-
styledTotalRow5("", "", "4 750 920", "4 750 920"));
|
|
92
|
-
const doc = parseXml(xmlDoc(buildArticle("etat_B", "État B", table)));
|
|
93
|
-
const result = parsePlfCredits(doc);
|
|
94
|
-
expect(result).toHaveLength(1);
|
|
95
|
-
expect(result[0].missions).toHaveLength(2);
|
|
96
|
-
const m1 = result[0].missions[0];
|
|
97
|
-
expect(m1.libelle).toBe("Action extérieure de l'État");
|
|
98
|
-
expect(m1.autorisationEngagementAnnule).toBe(73072877);
|
|
99
|
-
expect(m1.creditPaiementAnnule).toBe(68940645);
|
|
100
|
-
expect(m1.programmes[0].autorisationEngagementAnnule).toBe(49000000);
|
|
101
|
-
expect(result[0].total.autorisationEngagementAnnule).toBe(4750920);
|
|
102
|
-
expect(result[0].total.creditPaiementAnnule).toBe(4750920);
|
|
103
|
-
});
|
|
104
|
-
// ---------------------------------------------------------------------
|
|
105
|
-
// 6. État C avec table réelle (budgets annexes, format classé)
|
|
106
|
-
// ---------------------------------------------------------------------
|
|
107
|
-
it("parses État C with real table (budgets annexes)", () => {
|
|
108
|
-
const table = etatTable3Col("C", missionRow("Contrôle et exploitation aériens", "2 545 536 291", "2 425 536 292") +
|
|
109
|
-
programmeRow("Soutien aux prestations de l'aviation civile", "1 712 334 320", "1 697 584 442") +
|
|
110
|
-
lgncredRow("dont titre 2", "1 465 867 161", "1 465 867 161") +
|
|
111
|
-
totalRow("2 691 230 585", "2 572 949 963"));
|
|
112
|
-
const doc = parseXml(xmlDoc(buildArticle("etat_C", "État C", table)));
|
|
113
|
-
const result = parsePlfCredits(doc);
|
|
114
|
-
expect(result).toHaveLength(1);
|
|
115
|
-
expect(result[0].budgetType).toBe("budgetsAnnexes");
|
|
116
|
-
expect(result[0].libelle).toBe("Budgets annexes");
|
|
117
|
-
expect(result[0].missions).toHaveLength(1);
|
|
118
|
-
expect(result[0].missions[0].libelle).toBe("Contrôle et exploitation aériens");
|
|
119
|
-
expect(result[0].missions[0].programmes).toHaveLength(1);
|
|
120
|
-
expect(result[0].missions[0].programmes[0].lignes[0].libelle).toBe("dont titre 2");
|
|
121
|
-
});
|
|
122
|
-
// ---------------------------------------------------------------------
|
|
123
|
-
// 7. État D — 2 sous-tableaux CAS + CCF (format classé)
|
|
124
|
-
// ---------------------------------------------------------------------
|
|
125
|
-
it("parses État D with two sub-tables (CAS then CCF)", () => {
|
|
126
|
-
const articleBody = `
|
|
127
|
-
<alinea style="font-family: 'Courier New';font-weight: bold">
|
|
128
|
-
<num>I. –</num>
|
|
129
|
-
<alinea>
|
|
130
|
-
<content>${etatTable3Col("D", missionRow("Contrôle de la circulation et du stationnement routiers", "1 928 700 107", "1 928 700 107") +
|
|
131
|
-
programmeRow("Structures et dispositifs de sécurité routière", "344 340 107", "344 340 107") +
|
|
132
|
-
totalRow("77 975 293 936", "78 086 793 936"))}</content>
|
|
133
|
-
</alinea>
|
|
134
|
-
<content><p>I. – COMPTES D'AFFECTATION SPÉCIALE</p></content>
|
|
135
|
-
</alinea>
|
|
136
|
-
<alinea style="font-family: 'Courier New';font-weight: bold">
|
|
137
|
-
<num>II. –</num>
|
|
138
|
-
<alinea>
|
|
139
|
-
<content>${etatTable3Col("D", missionRow("Accords monétaires internationaux", "100 000 000", "100 000 000") +
|
|
140
|
-
programmeRow("Relations monétaires internationales", "100 000 000", "100 000 000") +
|
|
141
|
-
totalRow("150 236 603 151", "150 715 243 603"))}</content>
|
|
142
|
-
</alinea>
|
|
143
|
-
<content><p>II. – COMPTES DE CONCOURS FINANCIERS</p></content>
|
|
144
|
-
</alinea>`;
|
|
145
|
-
const doc = parseXml(xmlDoc(buildArticle("etat_D", "État D", articleBody)));
|
|
146
|
-
const result = parsePlfCredits(doc);
|
|
147
|
-
expect(result).toHaveLength(2);
|
|
148
|
-
expect(result[0].budgetType).toBe("comptesSpeciaux");
|
|
149
|
-
expect(result[0].libelle).toBe("Comptes d'affectation spéciale");
|
|
150
|
-
expect(result[0].missions[0].libelle).toBe("Contrôle de la circulation et du stationnement routiers");
|
|
151
|
-
expect(result[1].budgetType).toBe("comptesSpeciaux");
|
|
152
|
-
expect(result[1].libelle).toBe("Comptes de concours financiers");
|
|
153
|
-
expect(result[1].missions[0].libelle).toBe("Accords monétaires internationaux");
|
|
154
|
-
});
|
|
155
|
-
// ---------------------------------------------------------------------
|
|
156
|
-
// 8. Artefact dans les nombres
|
|
157
|
-
// ---------------------------------------------------------------------
|
|
158
|
-
it("handles literal &nbsp; artefacts in amounts", () => {
|
|
159
|
-
// Simulate the double-escaped entities found in Sénat XML:
|
|
160
|
-
// the raw file contains "2&nbsp;545" which XML parser resolves to text "2 545"
|
|
161
|
-
const doc = parseXml(xmlDoc(buildArticle("etat_B", "État B", etatTable3Col("B", missionRow("Test Mission", "2&nbsp;545&nbsp;536&nbsp;291", "2&nbsp;425&nbsp;536&nbsp;292") +
|
|
162
|
-
programmeRow("Test Programme", "1&nbsp;712&nbsp;334", "1&nbsp;697&nbsp;584")))));
|
|
163
|
-
const result = parsePlfCredits(doc);
|
|
164
|
-
expect(result).toHaveLength(1);
|
|
165
|
-
expect(result[0].missions[0].autorisationEngagement).toBe(2545536291);
|
|
166
|
-
expect(result[0].missions[0].creditPaiement).toBe(2425536292);
|
|
167
|
-
expect(result[0].missions[0].programmes[0].autorisationEngagement).toBe(1712334);
|
|
168
|
-
});
|
|
169
|
-
// ---------------------------------------------------------------------
|
|
170
|
-
// 9. Lignes supprimées
|
|
171
|
-
// ---------------------------------------------------------------------
|
|
172
|
-
it("ignores deleted rows (ligne supprimée)", () => {
|
|
173
|
-
const doc = parseXml(xmlDoc(buildArticle("etat_B", "État B", etatTable3Col("B", styledMissionRow("Action extérieure de l'État", "3 449 452 613", "3 454 425 325") +
|
|
174
|
-
styledProgrammeRow("Fonds d'urgence pour les Français de l'étranger (ligne supprimée)", "152 526 977", "152 930 677") +
|
|
175
|
-
styledMissionRow("Autre mission", "1 000 000", "2 000 000")))));
|
|
176
|
-
const result = parsePlfCredits(doc);
|
|
177
|
-
expect(result[0].missions).toHaveLength(2);
|
|
178
|
-
// The "Fonds d'urgence" programme should be excluded because it's a deleted line
|
|
179
|
-
expect(result[0].missions[0].programmes).toHaveLength(0);
|
|
180
|
-
});
|
|
181
|
-
// ---------------------------------------------------------------------
|
|
182
|
-
// 10. Lignes nouvelles (suffixe retiré du libellé)
|
|
183
|
-
// ---------------------------------------------------------------------
|
|
184
|
-
it("cleans up new-row suffix (ligne nouvelle)", () => {
|
|
185
|
-
const doc = parseXml(xmlDoc(buildArticle("etat_B", "État B", etatTable3Col("B", styledProgrammeRow("Nouvelle action (ligne nouvelle)", "5 000 000", "6 000 000")))));
|
|
186
|
-
const result = parsePlfCredits(doc);
|
|
187
|
-
expect(result[0].missions).toHaveLength(1);
|
|
188
|
-
expect(result[0].missions[0].libelle).toBe("Nouvelle action");
|
|
189
|
-
});
|
|
190
|
-
// ---------------------------------------------------------------------
|
|
191
|
-
// 11. Document sans body
|
|
192
|
-
// ---------------------------------------------------------------------
|
|
193
|
-
it("returns empty array for document without body", () => {
|
|
194
|
-
const doc = parseXml(`<?xml version="1.0" encoding="UTF-8"?>
|
|
195
|
-
<akomaNtoso xmlns="http://docs.oasis-open.org/legaldocml/ns/akn/3.0">
|
|
196
|
-
<bill><preamble><docTitle>Test</docTitle></preamble></bill>
|
|
197
|
-
</akomaNtoso>`);
|
|
198
|
-
expect(parsePlfCredits(doc)).toEqual([]);
|
|
199
|
-
});
|
|
200
|
-
// ---------------------------------------------------------------------
|
|
201
|
-
// 12. Multiple états B + C + D in same document
|
|
202
|
-
// ---------------------------------------------------------------------
|
|
203
|
-
it("parses all three états in a single document", () => {
|
|
204
|
-
const etatB = buildArticle("etat_B", "État B", etatTable3Col("B", missionRow("Mission Générale", "10 000 000", "20 000 000") +
|
|
205
|
-
programmeRow("Programme A", "5 000 000", "10 000 000") +
|
|
206
|
-
totalRow("10 000 000", "20 000 000")));
|
|
207
|
-
const etatC = buildArticle("etat_C", "État C", etatTable3Col("C", missionRow("Mission Annexe", "1 000 000", "2 000 000") +
|
|
208
|
-
programmeRow("Programme Annexe", "500 000", "1 000 000") +
|
|
209
|
-
totalRow("1 000 000", "2 000 000")));
|
|
210
|
-
const etatD = buildArticle("etat_D", "État D", `<alinea><num>I. –</num><alinea><content>${etatTable3Col("D", missionRow("Mission CAS", "100 000", "200 000") +
|
|
211
|
-
programmeRow("Programme CAS", "50 000", "100 000") +
|
|
212
|
-
totalRow("100 000", "200 000"))}</content></alinea>
|
|
213
|
-
<content><p>I. – COMPTES D'AFFECTATION SPÉCIALE</p></content></alinea>
|
|
214
|
-
<alinea><num>II. –</num><alinea><content>${etatTable3Col("D", missionRow("Mission CCF", "300 000", "400 000") +
|
|
215
|
-
programmeRow("Programme CCF", "150 000", "200 000") +
|
|
216
|
-
totalRow("300 000", "400 000"))}</content></alinea>
|
|
217
|
-
<content><p>II. – COMPTES DE CONCOURS FINANCIERS</p></content></alinea>`);
|
|
218
|
-
const doc = parseXml(xmlDoc(etatB + etatC + etatD));
|
|
219
|
-
const result = parsePlfCredits(doc);
|
|
220
|
-
expect(result).toHaveLength(4);
|
|
221
|
-
expect(result[0].budgetType).toBe("budgetGeneral");
|
|
222
|
-
expect(result[1].budgetType).toBe("budgetsAnnexes");
|
|
223
|
-
expect(result[2].budgetType).toBe("comptesSpeciaux");
|
|
224
|
-
expect(result[3].budgetType).toBe("comptesSpeciaux");
|
|
225
|
-
});
|
|
226
|
-
// ---------------------------------------------------------------------
|
|
227
|
-
// 13. Programme sans mission active → devient sa propre mission
|
|
228
|
-
// ---------------------------------------------------------------------
|
|
229
|
-
it("treats a programme row without active mission as its own mission", () => {
|
|
230
|
-
const doc = parseXml(xmlDoc(buildArticle("etat_B", "État B", etatTable3Col("B", programmeRow("Programme orphelin", "1 000 000", "2 000 000") +
|
|
231
|
-
totalRow("1 000 000", "2 000 000")))));
|
|
232
|
-
const result = parsePlfCredits(doc);
|
|
233
|
-
expect(result).toHaveLength(1);
|
|
234
|
-
// The orphan programme is promoted to a mission with no sub-programmes
|
|
235
|
-
expect(result[0].missions).toHaveLength(1);
|
|
236
|
-
expect(result[0].missions[0].libelle).toBe("Programme orphelin");
|
|
237
|
-
expect(result[0].missions[0].programmes).toHaveLength(0);
|
|
238
|
-
expect(result[0].missions[0].autorisationEngagement).toBe(1000000);
|
|
239
|
-
});
|
|
240
|
-
// ---------------------------------------------------------------------
|
|
241
|
-
// 14. Table vide (que des en-têtes) → pas de section
|
|
242
|
-
// ---------------------------------------------------------------------
|
|
243
|
-
it("skips a table that has only headers and no data rows", () => {
|
|
244
|
-
const doc = parseXml(xmlDoc(buildArticle("etat_B", "État B",
|
|
245
|
-
/* eslint-disable max-len */
|
|
246
|
-
`<alinea><content><table class="alinea-table etat-tableau" data:code="B">
|
|
247
|
-
<tr><td colspan="3" style="font-style: italic;border-bottom:1px solid black"><p>(En euros)</p></td></tr>
|
|
248
|
-
<tr><td style="font-weight: bold;border-bottom:1px solid black"><p>Mission / Programme</p></td><td style="font-weight: bold;border-bottom:1px solid black"><p>AE</p></td><td style="font-weight: bold;border-bottom:1px solid black"><p>CP</p></td></tr>
|
|
249
|
-
</table></content></alinea>`)));
|
|
250
|
-
expect(parsePlfCredits(doc)).toEqual([]);
|
|
251
|
-
});
|
|
252
|
-
// ---------------------------------------------------------------------
|
|
253
|
-
// 15. Montants négatifs
|
|
254
|
-
// ---------------------------------------------------------------------
|
|
255
|
-
it("parses negative amounts", () => {
|
|
256
|
-
const doc = parseXml(xmlDoc(buildArticle("etat_B", "État B", etatTable3Col("B", missionRow("Mission avec annulation", "-10 000 000", "-5 000 000") +
|
|
257
|
-
totalRow("-10 000 000", "-5 000 000")))));
|
|
258
|
-
const result = parsePlfCredits(doc);
|
|
259
|
-
expect(result).toHaveLength(1);
|
|
260
|
-
expect(result[0].missions[0].autorisationEngagement).toBe(-10000000);
|
|
261
|
-
expect(result[0].missions[0].creditPaiement).toBe(-5000000);
|
|
262
|
-
});
|
|
263
|
-
// ---------------------------------------------------------------------
|
|
264
|
-
// 16. État D — fallback positionnel si le label n'est pas trouvé
|
|
265
|
-
// ---------------------------------------------------------------------
|
|
266
|
-
it("falls back to positional CAS/CCF when label text is missing", () => {
|
|
267
|
-
const articleBody = `
|
|
268
|
-
<alinea>
|
|
269
|
-
<alinea>
|
|
270
|
-
<content>${etatTable3Col("D", missionRow("Mission sans label 1", "100 000", "200 000") +
|
|
271
|
-
totalRow("100 000", "200 000"))}</content>
|
|
272
|
-
</alinea>
|
|
273
|
-
</alinea>
|
|
274
|
-
<alinea>
|
|
275
|
-
<alinea>
|
|
276
|
-
<content>${etatTable3Col("D", missionRow("Mission sans label 2", "300 000", "400 000") +
|
|
277
|
-
totalRow("300 000", "400 000"))}</content>
|
|
278
|
-
</alinea>
|
|
279
|
-
</alinea>`;
|
|
280
|
-
const doc = parseXml(xmlDoc(buildArticle("etat_D", "État D", articleBody)));
|
|
281
|
-
const result = parsePlfCredits(doc);
|
|
282
|
-
expect(result).toHaveLength(2);
|
|
283
|
-
expect(result[0].libelle).toBe("Comptes d'affectation spéciale");
|
|
284
|
-
expect(result[1].libelle).toBe("Comptes de concours financiers");
|
|
285
|
-
});
|
|
286
|
-
// ---------------------------------------------------------------------
|
|
287
|
-
// 17. Plusieurs lignes "dont titre" pour un même programme
|
|
288
|
-
// ---------------------------------------------------------------------
|
|
289
|
-
it("attaches multiple dont-titre rows to the same programme", () => {
|
|
290
|
-
const doc = parseXml(xmlDoc(buildArticle("etat_B", "État B", etatTable3Col("B", missionRow("Mission Test", "10 000 000", "20 000 000") +
|
|
291
|
-
programmeRow("Programme Multi", "5 000 000", "10 000 000") +
|
|
292
|
-
lgncredRow("dont titre 2", "1 000 000", "1 000 000") +
|
|
293
|
-
lgncredRow("dont CAS Pensions", "500 000", "500 000")))));
|
|
294
|
-
const result = parsePlfCredits(doc);
|
|
295
|
-
expect(result[0].missions[0].programmes[0].lignes).toHaveLength(2);
|
|
296
|
-
expect(result[0].missions[0].programmes[0].lignes[0].libelle).toBe("dont titre 2");
|
|
297
|
-
expect(result[0].missions[0].programmes[0].lignes[1].libelle).toBe("dont CAS Pensions");
|
|
298
|
-
});
|
|
299
|
-
// ---------------------------------------------------------------------
|
|
300
|
-
// 18. État D — 3 tables (la 3e sans label ni fallback → ignorée)
|
|
301
|
-
// ---------------------------------------------------------------------
|
|
302
|
-
it("skips a third sub-table in État D (no positional fallback beyond index 1)", () => {
|
|
303
|
-
const articleBody = `
|
|
304
|
-
<alinea>
|
|
305
|
-
<alinea>
|
|
306
|
-
<content>${etatTable3Col("D", missionRow("Mission 1", "100 000", "200 000") +
|
|
307
|
-
totalRow("100 000", "200 000"))}</content>
|
|
308
|
-
</alinea>
|
|
309
|
-
<content><p>I. – COMPTES D'AFFECTATION SPÉCIALE</p></content>
|
|
310
|
-
</alinea>
|
|
311
|
-
<alinea>
|
|
312
|
-
<alinea>
|
|
313
|
-
<content>${etatTable3Col("D", missionRow("Mission 2", "300 000", "400 000") +
|
|
314
|
-
totalRow("300 000", "400 000"))}</content>
|
|
315
|
-
</alinea>
|
|
316
|
-
<content><p>II. – COMPTES DE CONCOURS FINANCIERS</p></content>
|
|
317
|
-
</alinea>
|
|
318
|
-
<alinea>
|
|
319
|
-
<alinea>
|
|
320
|
-
<content>${etatTable3Col("D", missionRow("Mission 3 (inconnue)", "500 000", "600 000") +
|
|
321
|
-
totalRow("500 000", "600 000"))}</content>
|
|
322
|
-
</alinea>
|
|
323
|
-
</alinea>`;
|
|
324
|
-
const doc = parseXml(xmlDoc(buildArticle("etat_D", "État D", articleBody)));
|
|
325
|
-
const result = parsePlfCredits(doc);
|
|
326
|
-
// Only the first 2 are kept; the 3rd has no label and no positional fallback
|
|
327
|
-
expect(result).toHaveLength(2);
|
|
328
|
-
});
|
|
329
|
-
// ---------------------------------------------------------------------
|
|
330
|
-
// 19. État avec <num> qui n'est pas B/C/D → ignoré
|
|
331
|
-
// ---------------------------------------------------------------------
|
|
332
|
-
it("ignores etat article whose num is not État B/C/D", () => {
|
|
333
|
-
const doc = parseXml(xmlDoc(buildArticle("etat_A", "État A", `<alinea><content><table class="alinea-table etat-tableau" data:code="A">
|
|
334
|
-
<tr><td colspan="3"><p>(En euros)</p></td></tr>
|
|
335
|
-
<tr><td style="font-weight: bold"><p>Recettes</p></td><td><p>100</p></td><td><p>200</p></td></tr>
|
|
336
|
-
</table></content></alinea>`)));
|
|
337
|
-
expect(parsePlfCredits(doc)).toEqual([]);
|
|
338
|
-
});
|
|
339
|
-
// ---------------------------------------------------------------------
|
|
340
|
-
// 20. Mission supprimée (libelle undefined) → sautée
|
|
341
|
-
// ---------------------------------------------------------------------
|
|
342
|
-
it("skips a mission row with suppressed libelle", () => {
|
|
343
|
-
const doc = parseXml(xmlDoc(buildArticle("etat_B", "État B", etatTable3Col("B", styledMissionRow("Mission supprimée (ligne supprimée)", "1 000 000", "2 000 000") +
|
|
344
|
-
programmeRow("Programme sous mission supprimée", "500 000", "1 000 000") +
|
|
345
|
-
styledMissionRow("Mission valide", "3 000 000", "4 000 000")))));
|
|
346
|
-
const result = parsePlfCredits(doc);
|
|
347
|
-
// Mission "supprimée" is skipped; its programmes are also dropped because
|
|
348
|
-
// currentMission is null when the programme row is processed, so it becomes
|
|
349
|
-
// its own orphan mission
|
|
350
|
-
expect(result).toHaveLength(1);
|
|
351
|
-
expect(result[0].missions).toHaveLength(2);
|
|
352
|
-
expect(result[0].missions[0].libelle).toBe("Programme sous mission supprimée");
|
|
353
|
-
expect(result[0].missions[1].libelle).toBe("Mission valide");
|
|
354
|
-
});
|
|
355
|
-
// ---------------------------------------------------------------------
|
|
356
|
-
// 21. Cellules vides (<td/>) → montants undefined
|
|
357
|
-
// ---------------------------------------------------------------------
|
|
358
|
-
it("returns undefined amounts for empty td cells", () => {
|
|
359
|
-
const doc = parseXml(xmlDoc(buildArticle("etat_B", "État B", etatTable3Col("B", missionRow("Mission sans montants", "", "") +
|
|
360
|
-
totalRow("", "")))));
|
|
361
|
-
const result = parsePlfCredits(doc);
|
|
362
|
-
expect(result[0].missions[0].autorisationEngagement).toBeUndefined();
|
|
363
|
-
expect(result[0].missions[0].creditPaiement).toBeUndefined();
|
|
364
|
-
expect(result[0].total.autorisationEngagement).toBeUndefined();
|
|
365
|
-
});
|
|
366
|
-
// ---------------------------------------------------------------------
|
|
367
|
-
// 22. normalizeAmount: plus d'une virgule → undefined
|
|
368
|
-
// ---------------------------------------------------------------------
|
|
369
|
-
it("returns undefined for amounts with multiple commas", () => {
|
|
370
|
-
const doc = parseXml(xmlDoc(buildArticle("etat_B", "État B", etatTable3Col("B", missionRow("Test", "1,234,567", "890")))));
|
|
371
|
-
const result = parsePlfCredits(doc);
|
|
372
|
-
expect(result[0].missions[0].autorisationEngagement).toBeUndefined();
|
|
373
|
-
expect(result[0].missions[0].creditPaiement).toBe(890);
|
|
374
|
-
});
|
|
375
|
-
// ---------------------------------------------------------------------
|
|
376
|
-
// 23. normalizeAmount: non-breaking space (U+00A0)
|
|
377
|
-
// ---------------------------------------------------------------------
|
|
378
|
-
it("handles actual non-breaking-space characters (U+00A0)", () => {
|
|
379
|
-
const doc = parseXml(xmlDoc(buildArticle("etat_B", "État B", etatTable3Col("B", missionRow("Test", "3\u00A0449\u00A0452\u00A0613", "890")))));
|
|
380
|
-
const result = parsePlfCredits(doc);
|
|
381
|
-
expect(result[0].missions[0].autorisationEngagement).toBe(3449452613);
|
|
382
|
-
});
|
|
383
|
-
// ---------------------------------------------------------------------
|
|
384
|
-
// 24. État D — label sans correspondance → repli positionnel
|
|
385
|
-
// ---------------------------------------------------------------------
|
|
386
|
-
it("falls back to positional when label text doesn't match CAS or CCF", () => {
|
|
387
|
-
const articleBody = `
|
|
388
|
-
<alinea>
|
|
389
|
-
<alinea>
|
|
390
|
-
<content>${etatTable3Col("D", missionRow("Mission 1", "100 000", "200 000") +
|
|
391
|
-
totalRow("100 000", "200 000"))}</content>
|
|
392
|
-
</alinea>
|
|
393
|
-
<content><p>I. – AUTRE LABEL INCONNU</p></content>
|
|
394
|
-
</alinea>
|
|
395
|
-
<alinea>
|
|
396
|
-
<alinea>
|
|
397
|
-
<content>${etatTable3Col("D", missionRow("Mission 2", "300 000", "400 000") +
|
|
398
|
-
totalRow("300 000", "400 000"))}</content>
|
|
399
|
-
</alinea>
|
|
400
|
-
<content><p>II. – ENCORE UN AUTRE</p></content>
|
|
401
|
-
</alinea>`;
|
|
402
|
-
const doc = parseXml(xmlDoc(buildArticle("etat_D", "État D", articleBody)));
|
|
403
|
-
const result = parsePlfCredits(doc);
|
|
404
|
-
expect(result).toHaveLength(2);
|
|
405
|
-
expect(result[0].libelle).toBe("Comptes d'affectation spéciale");
|
|
406
|
-
expect(result[1].libelle).toBe("Comptes de concours financiers");
|
|
407
|
-
});
|
|
408
|
-
// ---------------------------------------------------------------------
|
|
409
|
-
// 25. Ligne dans un tableau avec < 3 td → ignorée
|
|
410
|
-
// ---------------------------------------------------------------------
|
|
411
|
-
it("skips rows with fewer than 3 td cells", () => {
|
|
412
|
-
const doc = parseXml(xmlDoc(buildArticle("etat_B", "État B",
|
|
413
|
-
/* eslint-disable max-len */
|
|
414
|
-
`<alinea><content><table class="alinea-table etat-tableau" data:code="B">
|
|
415
|
-
<tr><td colspan="3" style="font-style: italic;border-bottom:1px solid black"><p>(En euros)</p></td></tr>
|
|
416
|
-
<tr><td style="font-weight: bold;border-bottom:1px solid black"><p>Mission / Programme</p></td><td style="font-weight: bold;border-bottom:1px solid black"><p>AE</p></td><td style="font-weight: bold;border-bottom:1px solid black"><p>CP</p></td></tr>
|
|
417
|
-
<tr><td style="border-top:1px solid black;font-weight: bold"><p>Mission valide</p></td><td style="font-weight: bold"><p>1 000</p></td><td style="font-weight: bold"><p>2 000</p></td></tr>
|
|
418
|
-
<tr><td colspan="3"><p>Sous-titre sans données</p></td></tr>
|
|
419
|
-
<tr><td style="border-top:0px solid black"><p>Programme</p></td><td><p>500</p></td><td><p>600</p></td></tr>
|
|
420
|
-
<tr><td style="font-weight: bold;border-top:1px solid black;border-bottom:1px solid black"><p>Total</p></td><td style="font-weight: bold"><p>1 500</p></td><td style="font-weight: bold"><p>2 600</p></td></tr>
|
|
421
|
-
</table></content></alinea>`)));
|
|
422
|
-
const result = parsePlfCredits(doc);
|
|
423
|
-
expect(result).toHaveLength(1);
|
|
424
|
-
expect(result[0].missions).toHaveLength(1);
|
|
425
|
-
expect(result[0].missions[0].programmes).toHaveLength(1);
|
|
426
|
-
});
|
|
427
|
-
// ---------------------------------------------------------------------
|
|
428
|
-
// 26. 5 colonnes — format classé
|
|
429
|
-
// ---------------------------------------------------------------------
|
|
430
|
-
it("parses 5-column table with semantic classes", () => {
|
|
431
|
-
const table = etatTable5Col("B", missionRow5("Mission 5col", "1 000", "2 000", "100", "200") +
|
|
432
|
-
programmeRow5("Programme 5col", "500", "600", "50", "60") +
|
|
433
|
-
lgncredRow5("dont titre 2", "100", "100", "", "") +
|
|
434
|
-
totalRow5("1 000", "2 000", "100", "200"));
|
|
435
|
-
const doc = parseXml(xmlDoc(buildArticle("etat_B", "État B", table)));
|
|
436
|
-
const result = parsePlfCredits(doc);
|
|
437
|
-
expect(result).toHaveLength(1);
|
|
438
|
-
const m = result[0].missions[0];
|
|
439
|
-
expect(m.autorisationEngagement).toBe(1000);
|
|
440
|
-
expect(m.autorisationEngagementAnnule).toBe(100);
|
|
441
|
-
expect(m.creditPaiementAnnule).toBe(200);
|
|
442
|
-
expect(m.programmes[0].autorisationEngagementAnnule).toBe(50);
|
|
443
|
-
expect(m.programmes[0].creditPaiementAnnule).toBe(60);
|
|
444
|
-
expect(result[0].total.autorisationEngagementAnnule).toBe(100);
|
|
445
|
-
});
|
|
446
|
-
// ---------------------------------------------------------------------
|
|
447
|
-
// 27. Trailing dot-space sequences stripped from libelle
|
|
448
|
-
// ---------------------------------------------------------------------
|
|
449
|
-
it("strips trailing dot-space sequences from libelle", () => {
|
|
450
|
-
const doc = parseXml(xmlDoc(buildArticle("etat_B", "État B", etatTable3Col("B", missionRow("Mission X. . . . . . . . . . . . . . . . . .", "1 000 000", "2 000 000") +
|
|
451
|
-
programmeRow("Programme Y. . . . . . . . . . . . . . . . . . . . . . . .", "500 000", "600 000") +
|
|
452
|
-
lgncredRow("dont titre 2. . . . . . . . . . . . . . . . . . . . . . . .", "100 000", "100 000")))));
|
|
453
|
-
const result = parsePlfCredits(doc);
|
|
454
|
-
expect(result[0].missions[0].libelle).toBe("Mission X");
|
|
455
|
-
expect(result[0].missions[0].programmes[0].libelle).toBe("Programme Y");
|
|
456
|
-
expect(result[0].missions[0].programmes[0].lignes[0].libelle).toBe("dont titre 2");
|
|
457
|
-
});
|
|
458
|
-
});
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export declare function parseXml(xml: string): Document;
|
|
2
|
-
export declare function xmlDoc(body: string): string;
|
|
3
|
-
export declare function buildArticle(etatId: string, num: string, alineas: string): string;
|
|
4
|
-
export declare function missionRow(libelle: string, ae: string, cp: string): string;
|
|
5
|
-
export declare function missionRow5(libelle: string, ae: string, cp: string, aeAnnule: string, cpAnnule: string): string;
|
|
6
|
-
export declare function programmeRow(libelle: string, ae: string, cp: string): string;
|
|
7
|
-
export declare function programmeRow5(libelle: string, ae: string, cp: string, aeAnnule: string, cpAnnule: string): string;
|
|
8
|
-
export declare function lgncredRow(libelle: string, ae: string, cp: string): string;
|
|
9
|
-
export declare function lgncredRow5(libelle: string, ae: string, cp: string, aeAnnule: string, cpAnnule: string): string;
|
|
10
|
-
export declare function totalRow(ae: string, cp: string): string;
|
|
11
|
-
export declare function totalRow5(ae: string, cp: string, aeAnnule: string, cpAnnule: string): string;
|
|
12
|
-
export declare function styledMissionRow(libelle: string, ae: string, cp: string): string;
|
|
13
|
-
export declare function styledMissionRow5(libelle: string, ae: string, cp: string, aeAnnule: string, cpAnnule: string): string;
|
|
14
|
-
export declare function styledProgrammeRow(libelle: string, ae: string, cp: string): string;
|
|
15
|
-
export declare function styledProgrammeRow5(libelle: string, ae: string, cp: string, aeAnnule: string, cpAnnule: string): string;
|
|
16
|
-
export declare function styledDontTitreRow(ae: string, cp: string): string;
|
|
17
|
-
export declare function styledTotalRow(ae: string, cp: string): string;
|
|
18
|
-
export declare function styledTotalRow5(ae: string, cp: string, aeAnnule: string, cpAnnule: string): string;
|
|
19
|
-
export declare function etatTable3Col(dataCode: string, rows: string): string;
|
|
20
|
-
export declare function etatTable5Col(dataCode: string, rows: string): string;
|