@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 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>