@state-of-geneva/rapports-bdd 1.0.11
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/404.html +13 -0
- package/LICENSE +1 -0
- package/README.md +73 -0
- package/bin/rapports-bdd.js +43 -0
- package/index.html +13 -0
- package/package.json +63 -0
- package/scripts/generateFeatureFiles.js +134 -0
- package/scripts/postBuildCopy404.js +73 -0
- package/src/App.vue +38 -0
- package/src/assets/consts.ts +24 -0
- package/src/assets/main.css +18 -0
- package/src/components/CucumberExamples.vue +70 -0
- package/src/components/CucumberSteps.vue +63 -0
- package/src/components/CucumberTags.vue +48 -0
- package/src/generated/featureFiles.ts +4 -0
- package/src/generated/userConfig.ts +4 -0
- package/src/main.ts +62 -0
- package/src/models/cucumber-report-project.ts +5 -0
- package/src/models/cucumber-report.ts +4 -0
- package/src/models/menu-categorie.ts +7 -0
- package/src/router/index.ts +21 -0
- package/src/views/HomeView.vue +139 -0
- package/src/views/ProjectView.vue +417 -0
- package/tsconfig.app.json +14 -0
- package/tsconfig.json +11 -0
- package/tsconfig.node.json +19 -0
- package/vite.config.ts +22 -0
package/404.html
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="fr">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<link rel="icon" href="/logo.svg">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title>Rapports BDD</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="app"></div>
|
|
11
|
+
<script type="module" src="/src/main.ts"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
package/LICENSE
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
This work is licensed under the Creative Commons Attribution 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
|
package/README.md
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Rapports BDD
|
|
2
|
+
|
|
3
|
+
Une petite webapp Vue qui liste et affiche vos fichiers `.feature` Gherkin comme des rapports consultables par le métier.
|
|
4
|
+
|
|
5
|
+
## Prérequis
|
|
6
|
+
- Node.js >= 18
|
|
7
|
+
|
|
8
|
+
## Utilisation (consommateur)
|
|
9
|
+
Générez un site statique à partir de vos features.
|
|
10
|
+
|
|
11
|
+
Sans installation préalable (recommandé pour un essai rapide):
|
|
12
|
+
```sh
|
|
13
|
+
npx @state-of-geneva/rapports-bdd@latest --features ./features --out ./dist
|
|
14
|
+
# Optionnel si vous hébergez sous un sous-chemin
|
|
15
|
+
npx @state-of-geneva/rapports-bdd@latest --features ./features --out ./dist --base /mon-sous-chemin/
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Ou en tant que dépendance de dev:
|
|
19
|
+
```sh
|
|
20
|
+
npm i -D @state-of-geneva/rapports-bdd
|
|
21
|
+
npx rapports-bdd --features ./features --out ./dist
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
- `--features`: dossier source contenant vos `.feature`
|
|
25
|
+
- `--out`: dossier de sortie du site (ex: `dist`)
|
|
26
|
+
- `--base`: base URL si la webapp est servie sous un sous‑chemin
|
|
27
|
+
|
|
28
|
+
Important: la librairie ne contient ni features ni configuration « projet » — fournissez toujours `--features`.
|
|
29
|
+
|
|
30
|
+
## Démarrer plus vite avec le template
|
|
31
|
+
Le dépôt inclut un projet client prêt à l’emploi dans `template/`.
|
|
32
|
+
|
|
33
|
+
- Guide rapide (GitHub): https://github.com/republique-et-canton-de-geneve/afc-rapports-bdd/tree/main/template
|
|
34
|
+
- Exemples d’arborescences et détails: https://github.com/republique-et-canton-de-geneve/afc-rapports-bdd/blob/main/template/features/README.md
|
|
35
|
+
|
|
36
|
+
Le template sépare clairement ses commandes de celles de la librairie. Exécutez ses commandes depuis le dossier `template/`.
|
|
37
|
+
|
|
38
|
+
## Démo
|
|
39
|
+
GitHub Pages: https://republique-et-canton-de-geneve.github.io/afc-rapports-bdd/
|
|
40
|
+
|
|
41
|
+
## Organisation des features (résumé)
|
|
42
|
+
Deux modes sont supportés côté client:
|
|
43
|
+
- Multi‑projets (recommandé): `features/projects/<slug>/**/*.feature`
|
|
44
|
+
- Dossier simple (slug inféré): `features/*.feature` (le slug = nom du dossier)
|
|
45
|
+
|
|
46
|
+
## Configuration (rapports-bdd.config.json)
|
|
47
|
+
Pour personnaliser les noms/catégories/masquages, placez un fichier dans `features/rapports-bdd.config.json` côté projet client. Référez‑vous aux exemples:
|
|
48
|
+
- Doc: https://github.com/republique-et-canton-de-geneve/afc-rapports-bdd/blob/main/template/features/README.md
|
|
49
|
+
- Exemple de config: https://github.com/republique-et-canton-de-geneve/afc-rapports-bdd/blob/main/template/features/rapports-bdd.config.json
|
|
50
|
+
|
|
51
|
+
## Développement de la librairie (contributeurs)
|
|
52
|
+
Ces commandes sont pour travailler sur la librairie elle‑même (ne pas confondre avec le template):
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
npm install
|
|
56
|
+
npm run dev # serveur Vite pour développer l’UI
|
|
57
|
+
npm run build # génère le paquet (utilise scripts/generateFeatureFiles.js)
|
|
58
|
+
npm run lint
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Notes:
|
|
62
|
+
- Sans `FEATURES_DIR` (défini par le CLI), la génération produit des listes vides. C’est normal pour la librairie.
|
|
63
|
+
- Pour tester la librairie en conditions réelles, utilisez le dossier `template/` et ses scripts dédiés.
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
## Scripts de la librairie — à quoi servent-ils ?
|
|
67
|
+
- dev: démarre Vite pour développer l’UI de la librairie (contributeurs).
|
|
68
|
+
- build: enchaîne generate → build-only → postBuildCopy404. C’est la commande de référence utilisée par le CLI et la CI.
|
|
69
|
+
- build-only: lance uniquement `vite build`. Utile pour les contributeurs quand les fichiers générés existent déjà.
|
|
70
|
+
- generate: génère `src/generated/featureFiles.ts` et `src/generated/userConfig.ts` à partir de `FEATURES_DIR` (défini par le CLI). Sans `FEATURES_DIR`, génère des listes vides — c’est normal pour la librairie.
|
|
71
|
+
- preview: non fourni au niveau de la librairie (le code ne contient aucune feature). Utilisez le dossier `template/` (projet client) et son `npm run preview` pour voir un site réel.
|
|
72
|
+
|
|
73
|
+
Note: un hook `prepublishOnly` construit automatiquement le paquet avant `npm publish`.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/* eslint-env node */
|
|
3
|
+
import { spawnSync } from 'child_process'
|
|
4
|
+
import path from 'path'
|
|
5
|
+
import fs from 'fs'
|
|
6
|
+
import { fileURLToPath } from 'url'
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
9
|
+
const __dirname = path.dirname(__filename)
|
|
10
|
+
const pkgRoot = path.resolve(__dirname, '..')
|
|
11
|
+
|
|
12
|
+
function getArgValue(flag) {
|
|
13
|
+
const index = process.argv.indexOf(flag)
|
|
14
|
+
return index !== -1 ? process.argv[index + 1] : undefined
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const featuresPath = getArgValue('--features')
|
|
18
|
+
if (featuresPath) {
|
|
19
|
+
const resolvedFeatures = path.resolve(process.cwd(), featuresPath)
|
|
20
|
+
if (fs.existsSync(resolvedFeatures)) {
|
|
21
|
+
process.env.FEATURES_DIR = resolvedFeatures
|
|
22
|
+
} else {
|
|
23
|
+
console.warn(`[rapports-bdd] Features directory not found: ${resolvedFeatures}. Falling back to default public assets.`)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const outDir = getArgValue('--out')
|
|
28
|
+
if (outDir) {
|
|
29
|
+
process.env.OUT_DIR = path.resolve(process.cwd(), outDir)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const basePath = getArgValue('--base')
|
|
33
|
+
if (basePath) {
|
|
34
|
+
process.env.BASE_PATH = basePath
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
console.log(`[rapports-bdd] Using FEATURES_DIR="${process.env.FEATURES_DIR ?? ''}", OUT_DIR="${process.env.OUT_DIR ?? ''}", BASE_PATH="${process.env.BASE_PATH ?? ''}"`)
|
|
38
|
+
const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm'
|
|
39
|
+
const result = spawnSync(npmCmd, ['run', 'build'], { stdio: 'inherit', cwd: pkgRoot, env: { ...process.env }, shell: true })
|
|
40
|
+
if (result.error) {
|
|
41
|
+
console.error(`[rapports-bdd] Failed to run build: ${result.error.message}`)
|
|
42
|
+
}
|
|
43
|
+
process.exit(result.status ?? 1)
|
package/index.html
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="fr">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<link rel="icon" href="/logo.svg">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title>Rapports BDD</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="app"></div>
|
|
11
|
+
<script type="module" src="/src/main.ts"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@state-of-geneva/rapports-bdd",
|
|
3
|
+
"version": "1.0.11",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"bin": {
|
|
6
|
+
"rapports-bdd": "./bin/rapports-bdd.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin",
|
|
10
|
+
"scripts",
|
|
11
|
+
"src",
|
|
12
|
+
"index.html",
|
|
13
|
+
"404.html",
|
|
14
|
+
"vite.config.ts",
|
|
15
|
+
"tsconfig.json",
|
|
16
|
+
"tsconfig.app.json",
|
|
17
|
+
"tsconfig.node.json"
|
|
18
|
+
],
|
|
19
|
+
"license": "CC-BY-4.0",
|
|
20
|
+
"description": "CLI + Vue app to render Gherkin .feature files as browsable reports (library only, no features inside)",
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=18"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"dev": "vite",
|
|
29
|
+
"build": "npm run generate && npm run build-only && node scripts/postBuildCopy404.js",
|
|
30
|
+
"build-only": "vite build",
|
|
31
|
+
"type-check": "vue-tsc --build --force",
|
|
32
|
+
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
|
33
|
+
"format": "prettier --write src/",
|
|
34
|
+
"generate": "node scripts/generateFeatureFiles.js",
|
|
35
|
+
"prepublishOnly": "npm run build"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@cucumber/cucumber-json-converter": "^0.1.0",
|
|
39
|
+
"@cucumber/gherkin": "^28.0.0",
|
|
40
|
+
"@primevue/themes": "^4.0.0",
|
|
41
|
+
"primeicons": "^7.0.0",
|
|
42
|
+
"primevue": "^4.0.0",
|
|
43
|
+
"vue": "^3.4.21",
|
|
44
|
+
"vue-router": "^4.3.0",
|
|
45
|
+
"@vitejs/plugin-vue": "^5.0.5",
|
|
46
|
+
"vite": "^5.3.1"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@rushstack/eslint-patch": "^1.8.0",
|
|
50
|
+
"@tsconfig/node20": "^20.1.4",
|
|
51
|
+
"@types/node": "^20.14.5",
|
|
52
|
+
"@vue/eslint-config-prettier": "^9.0.0",
|
|
53
|
+
"@vue/eslint-config-typescript": "^13.0.0",
|
|
54
|
+
"@vue/tsconfig": "^0.5.1",
|
|
55
|
+
"eslint": "^8.57.0",
|
|
56
|
+
"eslint-plugin-vue": "^9.23.0",
|
|
57
|
+
"npm-run-all2": "^6.2.0",
|
|
58
|
+
"prettier": "^3.2.5",
|
|
59
|
+
"typescript": "~5.4.0",
|
|
60
|
+
"vite-plugin-vue-devtools": "^7.3.1",
|
|
61
|
+
"vue-tsc": "^2.0.21"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/* eslint-env node */
|
|
2
|
+
import { fileURLToPath } from 'url'
|
|
3
|
+
import path, { dirname } from 'path'
|
|
4
|
+
import fs from 'fs'
|
|
5
|
+
|
|
6
|
+
// Recréer __dirname en utilisant import.meta.url
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
8
|
+
const __dirname = dirname(__filename)
|
|
9
|
+
|
|
10
|
+
const BASE_PATH = process.env.BASE_PATH || '/'
|
|
11
|
+
const basePrefix = BASE_PATH === '/' ? '' : BASE_PATH.replace(/\/$/, '')
|
|
12
|
+
|
|
13
|
+
// Fonction pour parcourir les fichiers .feature
|
|
14
|
+
function findFeatureFiles(dir, baseDir, slugIfNoProjects) {
|
|
15
|
+
let results = []
|
|
16
|
+
|
|
17
|
+
// Si le dossier n'existe pas, retourner une liste vide
|
|
18
|
+
if (!fs.existsSync(dir)) {
|
|
19
|
+
return results
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
fs.readdirSync(dir).forEach((file) => {
|
|
23
|
+
const filePath = path.join(dir, file)
|
|
24
|
+
const stat = fs.statSync(filePath)
|
|
25
|
+
|
|
26
|
+
if (stat && stat.isDirectory()) {
|
|
27
|
+
// Récursion pour les sous-dossiers
|
|
28
|
+
results = results.concat(findFeatureFiles(filePath, baseDir, slugIfNoProjects))
|
|
29
|
+
} else if (filePath.endsWith('.feature')) {
|
|
30
|
+
// Générer un chemin relatif basé sur `baseDir`
|
|
31
|
+
const relativePath = '/' + path.relative(baseDir, filePath).replace(/\\/g, '/')
|
|
32
|
+
// Si le chemin ne commence pas par /projects/, et qu'on a un slug fourni,
|
|
33
|
+
// préfixer par /projects/<slug>
|
|
34
|
+
const needsProjectsPrefix = !relativePath.startsWith('/projects/') && !!slugIfNoProjects
|
|
35
|
+
const adjustedRelative = needsProjectsPrefix
|
|
36
|
+
? `/projects/${slugIfNoProjects}${relativePath}`
|
|
37
|
+
: relativePath
|
|
38
|
+
const fullPath = basePrefix + adjustedRelative
|
|
39
|
+
results.push(fullPath)
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
return results
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function findFeatureSlugs(featureFiles) {
|
|
47
|
+
return [...new Set(
|
|
48
|
+
featureFiles.map((featureFile) => {
|
|
49
|
+
const prefixPattern = basePrefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
50
|
+
const regex = new RegExp(`^${prefixPattern}/projects/([^/]+)`) // capture le slug après /projects/
|
|
51
|
+
const match = featureFile.match(regex)
|
|
52
|
+
return match ? match[1] : null
|
|
53
|
+
}).filter(slug => slug !== null)
|
|
54
|
+
)]
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Fonction pour générer le(s) fichier(s) ts
|
|
58
|
+
function generateFiles() {
|
|
59
|
+
// Définir le chemin du répertoire et des fichiers générés
|
|
60
|
+
const outputDirPath = path.join(__dirname, '../src/generated/')
|
|
61
|
+
const featureFilesTsPath = path.join(outputDirPath, 'featureFiles.ts')
|
|
62
|
+
const userConfigTsPath = path.join(outputDirPath, 'userConfig.ts')
|
|
63
|
+
|
|
64
|
+
// Vérifier si le répertoire existe, sinon le créer
|
|
65
|
+
if (!fs.existsSync(outputDirPath)) {
|
|
66
|
+
fs.mkdirSync(outputDirPath, { recursive: true })
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const inputDirPath = process.env.FEATURES_DIR
|
|
70
|
+
? path.resolve(process.env.FEATURES_DIR)
|
|
71
|
+
: null
|
|
72
|
+
|
|
73
|
+
// If no FEATURES_DIR was provided or directory is missing, generate empty datasets
|
|
74
|
+
if (!inputDirPath || !fs.existsSync(inputDirPath)) {
|
|
75
|
+
const featureFilesData = '//generated by generate-feature-list plugin\n' +
|
|
76
|
+
'export const FEATURE_FILES = []\n\n' +
|
|
77
|
+
'export const FEATURE_SLUGS = []'
|
|
78
|
+
fs.writeFileSync(featureFilesTsPath, featureFilesData)
|
|
79
|
+
const userConfigTs = '//generated by generate-feature-list plugin\n' +
|
|
80
|
+
'export const USER_TRANSLATIONS = []\n' +
|
|
81
|
+
'export const USER_DEFAULT_CATEGORY = null\n' +
|
|
82
|
+
'export const USER_IGNORED_SLUGS = []\n'
|
|
83
|
+
fs.writeFileSync(userConfigTsPath, userConfigTs)
|
|
84
|
+
console.log('[generateFeatureFiles] No FEATURES_DIR set; generated empty lists.')
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const hasProjectsSubfolder = fs.existsSync(path.join(inputDirPath, 'projects'))
|
|
89
|
+
const slugIfNoProjects = hasProjectsSubfolder ? undefined : path.basename(path.resolve(inputDirPath))
|
|
90
|
+
|
|
91
|
+
// 1) Générer les listes de features et slugs
|
|
92
|
+
const featureFiles = findFeatureFiles(inputDirPath, inputDirPath, slugIfNoProjects)
|
|
93
|
+
const featureSlugs = findFeatureSlugs(featureFiles)
|
|
94
|
+
const featureFilesData = '//generated by generate-feature-list plugin\n' +
|
|
95
|
+
'export const FEATURE_FILES = ' + JSON.stringify(featureFiles, null, 2) + '\n\n' +
|
|
96
|
+
'export const FEATURE_SLUGS = ' + JSON.stringify(featureSlugs, null, 2)
|
|
97
|
+
fs.writeFileSync(featureFilesTsPath, featureFilesData)
|
|
98
|
+
console.log(`Feature files list generated: ${featureFilesTsPath}`)
|
|
99
|
+
|
|
100
|
+
// 2) Générer un fichier de config utilisateur optionnel (userConfig.ts)
|
|
101
|
+
// Recherche d'un fichier rapports-bdd.config.json au niveau du répertoire des features
|
|
102
|
+
let userTranslations = []
|
|
103
|
+
let userDefaultCategory = null
|
|
104
|
+
let userIgnoredSlugs = []
|
|
105
|
+
try {
|
|
106
|
+
const configJsonPath = path.join(inputDirPath, 'rapports-bdd.config.json')
|
|
107
|
+
if (fs.existsSync(configJsonPath)) {
|
|
108
|
+
const raw = fs.readFileSync(configJsonPath, 'utf-8')
|
|
109
|
+
const parsed = JSON.parse(raw)
|
|
110
|
+
if (parsed && typeof parsed === 'object') {
|
|
111
|
+
if (Array.isArray(parsed.translations)) {
|
|
112
|
+
userTranslations = parsed.translations
|
|
113
|
+
}
|
|
114
|
+
if (typeof parsed.defaultCategory === 'string') {
|
|
115
|
+
userDefaultCategory = parsed.defaultCategory
|
|
116
|
+
}
|
|
117
|
+
if (Array.isArray(parsed.ignoredSlugs)) {
|
|
118
|
+
userIgnoredSlugs = parsed.ignoredSlugs
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
} catch (e) {
|
|
123
|
+
console.warn(`[generateFeatureFiles] Warning while reading rapports-bdd.config.json: ${e?.message || e}`)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const userConfigTs = '//generated by generate-feature-list plugin\n' +
|
|
127
|
+
'export const USER_TRANSLATIONS = ' + JSON.stringify(userTranslations, null, 2) + '\n' +
|
|
128
|
+
'export const USER_DEFAULT_CATEGORY = ' + JSON.stringify(userDefaultCategory) + '\n' +
|
|
129
|
+
'export const USER_IGNORED_SLUGS = ' + JSON.stringify(userIgnoredSlugs, null, 2) + '\n'
|
|
130
|
+
fs.writeFileSync(userConfigTsPath, userConfigTs)
|
|
131
|
+
console.log(`User config generated: ${userConfigTsPath}`)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
generateFiles()
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/* eslint-env node */
|
|
2
|
+
import { fileURLToPath } from 'url'
|
|
3
|
+
import path, { dirname } from 'path'
|
|
4
|
+
import fs from 'fs'
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
7
|
+
const __dirname = dirname(__filename)
|
|
8
|
+
|
|
9
|
+
// Determine the actual output directory used by Vite
|
|
10
|
+
const outDirEnv = process.env.OUT_DIR
|
|
11
|
+
const outDir = outDirEnv ? path.resolve(outDirEnv) : path.resolve(__dirname, '../dist')
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const indexPath = path.join(outDir, 'index.html')
|
|
15
|
+
const targetPath = path.join(outDir, '404.html')
|
|
16
|
+
|
|
17
|
+
if (fs.existsSync(indexPath)) {
|
|
18
|
+
// Ensure directory exists (Vite should have created it already)
|
|
19
|
+
fs.copyFileSync(indexPath, targetPath)
|
|
20
|
+
console.log(`[postBuildCopy404] Copied: ${indexPath} -> ${targetPath}`)
|
|
21
|
+
} else {
|
|
22
|
+
console.log(`[postBuildCopy404] index.html not found in ${outDir}. Skipping 404.html copy.`)
|
|
23
|
+
}
|
|
24
|
+
} catch (err) {
|
|
25
|
+
console.warn(`[postBuildCopy404] Warning: ${err?.message || err}`)
|
|
26
|
+
// Do not fail the build because of this convenience step
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Additional step: when FEATURES_DIR is a plain folder (no 'projects' subfolder),
|
|
30
|
+
// Vite places .feature files at the root of dist (keeping their relative paths).
|
|
31
|
+
// The app expects them under /projects/<slug>/. Mirror them accordingly.
|
|
32
|
+
try {
|
|
33
|
+
const featuresDirEnv = process.env.FEATURES_DIR ? path.resolve(process.env.FEATURES_DIR) : undefined
|
|
34
|
+
if (featuresDirEnv && fs.existsSync(featuresDirEnv)) {
|
|
35
|
+
const hasProjectsSubfolder = fs.existsSync(path.join(featuresDirEnv, 'projects'))
|
|
36
|
+
if (!hasProjectsSubfolder) {
|
|
37
|
+
const slug = path.basename(path.resolve(featuresDirEnv))
|
|
38
|
+
const distProjectsSlugDir = path.join(outDir, 'projects', slug)
|
|
39
|
+
|
|
40
|
+
// Recursively walk FEATURES_DIR to find all .feature files and mirror them into dist/projects/<slug>
|
|
41
|
+
const walk = (dir, baseDir) => {
|
|
42
|
+
const entries = fs.readdirSync(dir)
|
|
43
|
+
for (const entry of entries) {
|
|
44
|
+
const fullPath = path.join(dir, entry)
|
|
45
|
+
const stat = fs.statSync(fullPath)
|
|
46
|
+
if (stat.isDirectory()) {
|
|
47
|
+
walk(fullPath, baseDir)
|
|
48
|
+
} else if (fullPath.endsWith('.feature')) {
|
|
49
|
+
const relative = path.relative(baseDir, fullPath)
|
|
50
|
+
const srcInDist = path.join(outDir, relative)
|
|
51
|
+
const destInDist = path.join(distProjectsSlugDir, relative)
|
|
52
|
+
const destDir = path.dirname(destInDist)
|
|
53
|
+
if (!fs.existsSync(destDir)) {
|
|
54
|
+
fs.mkdirSync(destDir, { recursive: true })
|
|
55
|
+
}
|
|
56
|
+
if (fs.existsSync(srcInDist)) {
|
|
57
|
+
fs.copyFileSync(srcInDist, destInDist)
|
|
58
|
+
console.log(`[postBuildCopy404] Mirrored feature: ${srcInDist} -> ${destInDist}`)
|
|
59
|
+
} else {
|
|
60
|
+
// If vite didn't copy the source (e.g., different publicDir structure), skip silently
|
|
61
|
+
// to avoid failing the build.
|
|
62
|
+
// console.log(`[postBuildCopy404] Source not found in dist, skipping: ${srcInDist}`)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
walk(featuresDirEnv, featuresDirEnv)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
} catch (err) {
|
|
72
|
+
console.warn(`[postBuildCopy404] Mirroring warning: ${err?.message || err}`)
|
|
73
|
+
}
|
package/src/App.vue
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { RouterView } from 'vue-router'
|
|
3
|
+
</script>
|
|
4
|
+
|
|
5
|
+
<template>
|
|
6
|
+
<div id="app-container">
|
|
7
|
+
<header>
|
|
8
|
+
<h1>Rapports BDD</h1>
|
|
9
|
+
</header>
|
|
10
|
+
<main>
|
|
11
|
+
<RouterView />
|
|
12
|
+
</main>
|
|
13
|
+
</div>
|
|
14
|
+
</template>
|
|
15
|
+
|
|
16
|
+
<style scoped>
|
|
17
|
+
#app-container {
|
|
18
|
+
display: grid;
|
|
19
|
+
grid-auto-rows: auto 1fr;
|
|
20
|
+
grid-template-areas:
|
|
21
|
+
'header'
|
|
22
|
+
'main';
|
|
23
|
+
min-height: 100vh;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
header {
|
|
27
|
+
grid-area: header;
|
|
28
|
+
color: var(--primary);
|
|
29
|
+
background-color: var(--accent);
|
|
30
|
+
text-align: center;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
main {
|
|
34
|
+
grid-area: main;
|
|
35
|
+
color: var(--secondary);
|
|
36
|
+
background-color: var(--primary);
|
|
37
|
+
}
|
|
38
|
+
</style>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type CucumberReportProject from '@/models/cucumber-report-project'
|
|
2
|
+
import { FEATURE_SLUGS } from '@/generated/featureFiles'
|
|
3
|
+
import { USER_TRANSLATIONS, USER_DEFAULT_CATEGORY, USER_IGNORED_SLUGS } from '@/generated/userConfig'
|
|
4
|
+
|
|
5
|
+
// Library must stay generic: no built-in project-specific ignores
|
|
6
|
+
const BUILTIN_IGNORED_SLUGS: string[] = []
|
|
7
|
+
const CATEGORIE_PAR_DEFAUT = USER_DEFAULT_CATEGORY ?? 'Orphelins'
|
|
8
|
+
|
|
9
|
+
const USER_TRANSLATIONS_TYPED: CucumberReportProject[] = USER_TRANSLATIONS as unknown as CucumberReportProject[]
|
|
10
|
+
const ALL_IGNORED = [...BUILTIN_IGNORED_SLUGS, ...USER_IGNORED_SLUGS]
|
|
11
|
+
|
|
12
|
+
const CUCUMBER_REPORTS: CucumberReportProject[] = FEATURE_SLUGS
|
|
13
|
+
.filter((slug) => !ALL_IGNORED.includes(slug))
|
|
14
|
+
.map((slug) => {
|
|
15
|
+
const translation = USER_TRANSLATIONS_TYPED.find((cucumberReport) => cucumberReport.slug === slug)
|
|
16
|
+
if (translation) {
|
|
17
|
+
return translation
|
|
18
|
+
} else {
|
|
19
|
+
return { name: slug, slug, categorie: CATEGORIE_PAR_DEFAUT }
|
|
20
|
+
}
|
|
21
|
+
})
|
|
22
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
23
|
+
|
|
24
|
+
export { CUCUMBER_REPORTS }
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--primary: #eff0f0;
|
|
3
|
+
--secondary: #173647;
|
|
4
|
+
--accent: #1dbb68;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
#app,
|
|
8
|
+
body,
|
|
9
|
+
html {
|
|
10
|
+
min-height: 100vh;
|
|
11
|
+
margin: 0;
|
|
12
|
+
padding: 0;
|
|
13
|
+
font-family: Arial, sans-serif;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.p-tree-node-toggle-button {
|
|
17
|
+
align-self: start;
|
|
18
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="example" v-for="example in examples" :key="example.id">
|
|
3
|
+
|
|
4
|
+
<div class="title">
|
|
5
|
+
<b>{{ example.keyword }}: </b> {{ example.name }}
|
|
6
|
+
</div>
|
|
7
|
+
<table>
|
|
8
|
+
<thead v-if="example.tableHeader">
|
|
9
|
+
<tr>
|
|
10
|
+
<th v-for="cell in example.tableHeader.cells" :key="cell.value">
|
|
11
|
+
{{cell.value}}
|
|
12
|
+
</th>
|
|
13
|
+
</tr>
|
|
14
|
+
</thead>
|
|
15
|
+
<tbody>
|
|
16
|
+
<tr v-for="row in example.tableBody" :key="row.id">
|
|
17
|
+
<td v-for="cell in row.cells" :key="cell.value">
|
|
18
|
+
{{cell.value}}
|
|
19
|
+
</td>
|
|
20
|
+
</tr>
|
|
21
|
+
</tbody>
|
|
22
|
+
</table>
|
|
23
|
+
</div>
|
|
24
|
+
</template>
|
|
25
|
+
<script setup lang="ts">
|
|
26
|
+
import type { PropType } from 'vue'
|
|
27
|
+
import * as Messages from '@cucumber/messages'
|
|
28
|
+
defineProps({
|
|
29
|
+
examples: {
|
|
30
|
+
type: Object as PropType<Array<Messages.Examples>>,
|
|
31
|
+
required: true,
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
</script>
|
|
35
|
+
<style scoped>
|
|
36
|
+
.title{
|
|
37
|
+
padding-bottom: 5px;
|
|
38
|
+
}
|
|
39
|
+
.example{
|
|
40
|
+
padding-top: 10px;
|
|
41
|
+
}
|
|
42
|
+
table {
|
|
43
|
+
display: table;
|
|
44
|
+
border-collapse: separate;
|
|
45
|
+
box-sizing: border-box;
|
|
46
|
+
text-indent: initial;
|
|
47
|
+
unicode-bidi: isolate;
|
|
48
|
+
border-spacing: 2px;
|
|
49
|
+
border-color: gray;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
tr {
|
|
53
|
+
display: table-row;
|
|
54
|
+
vertical-align: inherit;
|
|
55
|
+
unicode-bidi: isolate;
|
|
56
|
+
border-color: inherit;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
td, th {
|
|
60
|
+
padding: .4em .6em;
|
|
61
|
+
border-width: 0 2px 0 0;
|
|
62
|
+
border-style: solid;
|
|
63
|
+
border-color: #000080;
|
|
64
|
+
color: #297bde;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
td:first-child, th:first-child {
|
|
68
|
+
border-width: 0 2px;
|
|
69
|
+
}
|
|
70
|
+
</style>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="step" v-for="step in steps" :key="step.id">
|
|
3
|
+
|
|
4
|
+
<div class="title">
|
|
5
|
+
<b>{{ step.keyword }}</b> {{ step.text }}
|
|
6
|
+
</div>
|
|
7
|
+
<table v-if="step.dataTable">
|
|
8
|
+
<tbody>
|
|
9
|
+
<tr v-for="(row, rowIndex) in step.dataTable.rows" :key="row.id ?? rowIndex">
|
|
10
|
+
<td v-for="(cell, cellIndex) in row.cells" :key="cell.value ?? cellIndex">
|
|
11
|
+
{{cell.value}}
|
|
12
|
+
</td>
|
|
13
|
+
</tr>
|
|
14
|
+
</tbody>
|
|
15
|
+
</table>
|
|
16
|
+
</div>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<script setup lang="ts">
|
|
20
|
+
import type { PropType } from 'vue'
|
|
21
|
+
import * as Messages from '@cucumber/messages'
|
|
22
|
+
defineProps({
|
|
23
|
+
steps: {
|
|
24
|
+
type: Object as PropType<Array<Messages.Step>>,
|
|
25
|
+
required: true,
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
</script>
|
|
29
|
+
<style scoped>
|
|
30
|
+
|
|
31
|
+
.step {
|
|
32
|
+
padding-left: 20px;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
table {
|
|
36
|
+
display: table;
|
|
37
|
+
border-collapse: separate;
|
|
38
|
+
box-sizing: border-box;
|
|
39
|
+
text-indent: initial;
|
|
40
|
+
unicode-bidi: isolate;
|
|
41
|
+
border-spacing: 2px;
|
|
42
|
+
border-color: gray;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
tr {
|
|
46
|
+
display: table-row;
|
|
47
|
+
vertical-align: inherit;
|
|
48
|
+
unicode-bidi: isolate;
|
|
49
|
+
border-color: inherit;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
td, th {
|
|
53
|
+
padding: .4em .6em;
|
|
54
|
+
border-width: 0 2px 0 0;
|
|
55
|
+
border-style: solid;
|
|
56
|
+
border-color: #000080;
|
|
57
|
+
color: #297bde;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
td:first-child, th:first-child {
|
|
61
|
+
border-width: 0 2px;
|
|
62
|
+
}
|
|
63
|
+
</style>
|