Skip to content

Commit 991241e

Browse files
committed
feat: add integrabook for visual integration testing
1 parent ab934da commit 991241e

19 files changed

Lines changed: 2611 additions & 0 deletions
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Sync integrabook
2+
3+
on:
4+
push:
5+
branches: [main, feat/integrabook]
6+
paths: [integrabook/**]
7+
8+
jobs:
9+
sync:
10+
runs-on: ubuntu-latest
11+
permissions:
12+
contents: read
13+
steps:
14+
- uses: actions/checkout@v4
15+
with:
16+
sparse-checkout: integrabook
17+
18+
- uses: actions/checkout@v4
19+
with:
20+
repository: incubateur-ademe/impactco2-integrabook
21+
path: target
22+
token: ${{ secrets.INTEGRABOOK_SYNC_TOKEN }}
23+
24+
- name: Sync files
25+
env:
26+
COMMIT_MSG: ${{ github.event.head_commit.message }}
27+
run: |
28+
rsync -a --delete --exclude='.git' integrabook/ target/
29+
cd target
30+
git add -A
31+
if git diff --cached --quiet; then
32+
echo "No changes to sync"
33+
else
34+
git -c user.name="impactco2-sync" -c user.email="noreply@impactco2.fr" commit -m "sync: $COMMIT_MSG"
35+
git push
36+
fi

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
22

integrabook/README.md

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# Intégrabook - Impact CO2
2+
3+
Pages HTML de test simulant un site tiers qui intègre les modules Impact CO2.
4+
Destiné aux audits de sécurité (pentest) et aux tests d'intégration cross-origin.
5+
6+
## Démarrage rapide
7+
8+
1. Modifier `config.js` pour cibler l'environnement voulu :
9+
10+
```js
11+
const INTEGRABOOK_CONFIG = {
12+
baseUrl: 'https://impactco2.fr', // ou staging, localhost:3000, etc.
13+
theme: 'default', // 'default' | 'night'
14+
language: 'fr', // 'fr' | 'en' | 'es'
15+
}
16+
```
17+
18+
2. Servir les fichiers depuis le dossier `integrabook/` sur un domaine distinct (pour tester le CORS) :
19+
20+
```bash
21+
cd integrabook
22+
python3 -m http.server 8888
23+
# ou
24+
npx serve -p 8888
25+
```
26+
27+
3. Ouvrir `http://localhost:8888` dans un navigateur.
28+
29+
## Configuration
30+
31+
### Priorité de résolution
32+
33+
La configuration suit cet ordre de priorité (le premier trouvé gagne) :
34+
35+
1. **Query string** `?baseUrl=X&theme=Y&language=Z`
36+
2. **localStorage** (persisté via la barre de config UI)
37+
3. **`config.js`** (valeurs par défaut)
38+
39+
### Via `config.js` (recommandé pour l'environnement)
40+
41+
Le fichier `config.js` à la racine définit les valeurs par défaut. C'est le seul fichier à modifier pour changer d'environnement.
42+
43+
| Clé | Description | Valeurs |
44+
|------------|--------------------------------------|----------------------------------|
45+
| `baseUrl` | URL de l'instance Impact CO2 cible | URL complète sans slash final |
46+
| `theme` | Thème par défaut des widgets | `default`, `night` |
47+
| `language` | Langue par défaut | `fr`, `en`, `es` |
48+
49+
### Via query string (recommandé pour le partage)
50+
51+
Les paramètres `baseUrl`, `theme` et `language` dans l'URL sont propagés automatiquement sur tous les liens internes. Un lien partagé configure tout le site en un clic :
52+
53+
```
54+
http://pentest.example.com/?baseUrl=https://staging.impactco2.fr&theme=night&language=en
55+
```
56+
57+
### Via l'interface
58+
59+
Chaque page expose une barre de configuration en haut. Les changements sont persistés en localStorage **et** dans le query string.
60+
61+
Pour revenir aux valeurs de `config.js`, vider le localStorage :
62+
63+
```js
64+
localStorage.removeItem('integrabook-baseUrl')
65+
localStorage.removeItem('integrabook-theme')
66+
localStorage.removeItem('integrabook-language')
67+
```
68+
69+
### Thème night
70+
71+
Quand le thème `night` est sélectionné, les zones de rendu live passent automatiquement en fond sombre avec texte clair, pour refléter le comportement des widgets en mode nuit.
72+
73+
## Pages disponibles
74+
75+
### Modules thématiques
76+
77+
| Page | Module | Intégrations |
78+
|-----------------------|-----------------------------------------|---------------------------------------|
79+
| `comparateur.html` | Comparateur CO2 + étiquettes | iframe.js, iframe direct |
80+
| `transport.html` | Transport + itinéraire | iframe.js, iframe direct, API REST |
81+
| `livraison.html` | Livraison + étiquettes | iframe.js, iframe direct |
82+
| `alimentation.html` | Alimentation | iframe.js, iframe direct, API REST |
83+
| `habillement.html` | Habillement + Osez Changer | iframe.js, iframe direct |
84+
| `chauffage.html` | Chauffage | iframe.js, iframe direct, API REST |
85+
| `quiz.html` | Quiz + quiz infographie | iframe.js, iframe direct |
86+
| `infographie.html` | Infographie + détecteur | iframe.js, iframe direct |
87+
88+
### Méthodes d'intégration transverses
89+
90+
| Page | Méthode | Description |
91+
|-----------------------|-----------------------------------------|---------------------------------------|
92+
| `detecteur.html` | Scripts de détection | `detection.js` (sync) + `detection-async.js` |
93+
| `webcomponent.html` | Web Component `<etiquette-ico2>` | Custom element natif |
94+
| `api.html` | API REST `/api/v1/*` | Tous les endpoints avec testeur |
95+
| `legacy.html` | IDs legacy | `mon-impact-transport`, `datagir-*`, `ecolab-*`, etc. |
96+
97+
## Structure des fichiers
98+
99+
```
100+
integrabook/
101+
├── config.js # Configuration (base URL, theme, langue)
102+
├── shared.css # Styles communs
103+
├── shared.js # Logique partagée (config bar, params editor, helpers)
104+
├── index.html # Hub de navigation
105+
├── comparateur.html
106+
├── transport.html
107+
├── livraison.html
108+
├── alimentation.html
109+
├── habillement.html
110+
├── chauffage.html
111+
├── quiz.html
112+
├── infographie.html
113+
├── detecteur.html
114+
├── webcomponent.html
115+
├── api.html
116+
└── legacy.html
117+
```
118+
119+
## Fonctionnalités par page
120+
121+
Chaque page de module contient :
122+
123+
- **Barre de configuration** : base URL, thème, langue (persisté en localStorage + query string)
124+
- **Éditeur de paramètres** : formulaire spécifique au module avec Appliquer / Reset
125+
- **Bloc de code** : snippet d'intégration copiable, mis à jour en temps réel avec les paramètres
126+
- **Zone de rendu live** : le widget tel qu'un intégrateur tiers le verrait (fond adapté au thème)
127+
128+
### Détecteur (spécificités)
129+
130+
La page `detecteur.html` fonctionne différemment des autres : les scripts de détection scannent tout le DOM à la recherche de textes contenant des valeurs CO2 (regex sur "X kg CO2", "X tonnes CO2e", etc.) et les annotent avec des étiquettes interactives.
131+
132+
- **Sync** (`detection.js`) : se déclenche au clic sur "Charger". Le script s'exécute immédiatement.
133+
- **Async** (`detection-async.js`) : se charge au clic, puis se déclenche manuellement via `window.impactCO2Detection()`.
134+
- Les deux variantes sont **mutuellement exclusives** : la première qui tourne marque les éléments comme traités. Recharger la page entre les tests.
135+
136+
## Surfaces d'attaque couvertes
137+
138+
| Vecteur | Pages concernées |
139+
|----------------------------|-----------------------------------------------|
140+
| Injection de script tiers | Toutes les pages iframe (`iframe.js`) |
141+
| Cross-origin iframe | Toutes les pages avec iframe direct |
142+
| CORS / API REST | `api.html`, `transport.html`, `alimentation.html`, `chauffage.html` |
143+
| postMessage | Toutes les pages iframe (`iframe-resizer`) |
144+
| Custom elements | `webcomponent.html` |
145+
| DOM auto-detection | `detecteur.html`, `legacy.html` |

integrabook/alimentation.html

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<!DOCTYPE html>
2+
<html lang="fr">
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<title>Alimentation - Intégrabook</title>
8+
<link rel="stylesheet" href="shared.css">
9+
</head>
10+
11+
<body>
12+
<div class="layout">
13+
<header>
14+
<h1>Alimentation <span class="badge badge-iframe">iframe</span> <span class="badge badge-api">api</span></h1>
15+
<a class="back" href="index.html">← Intégrabook</a>
16+
</header>
17+
18+
<div id="config-bar" class="config-bar"></div>
19+
20+
<!-- Alimentation iframe -->
21+
<div class="section">
22+
<h2>Alimentation (via script iframe.js)</h2>
23+
<p class="desc">Comparateur d'impact carbone alimentaire.</p>
24+
<div id="params-alimentation"></div>
25+
<div id="code-alimentation" class="code-block"></div>
26+
<div id="render-alimentation" class="render-zone"></div>
27+
</div>
28+
29+
<!-- Iframe direct -->
30+
<div class="section">
31+
<h2>Alimentation (iframe direct)</h2>
32+
<p class="desc">Iframe brut sans script intermédiaire.</p>
33+
<div id="code-direct" class="code-block"></div>
34+
<div id="render-direct" class="render-zone"></div>
35+
</div>
36+
37+
<!-- API Alimentation -->
38+
<div class="section">
39+
<h2>API REST /api/v1/alimentation</h2>
40+
<p class="desc">Appel direct pour récupérer les données alimentaires par catégorie.</p>
41+
<div id="code-api" class="code-block"></div>
42+
<div class="api-controls">
43+
<button onclick="fetchApi('alimentation?category=group', 'api-result')">Par groupe</button>
44+
<button onclick="fetchApi('alimentation?category=rayon', 'api-result')">Par rayon</button>
45+
<button onclick="fetchApi('alimentation?category=popularity', 'api-result')">Par popularité</button>
46+
<button onclick="fetchApi('alimentation?category=group&language=en', 'api-result')">Par groupe (EN)</button>
47+
</div>
48+
<div id="api-result" class="api-result">Cliquez sur un bouton pour lancer un appel API.</div>
49+
</div>
50+
</div>
51+
52+
<script src="config.js"></script>
53+
<script src="shared.js"></script>
54+
<script>
55+
function renderAlimentation(extra) {
56+
const base = getBaseUrl()
57+
const search = buildSearch(extra)
58+
renderCodeBlock('code-alimentation',
59+
`<script
60+
data-name="impact-co2"
61+
src="${base}/iframe.js"
62+
data-type="alimentation"
63+
data-search="${search}"><\/script>`)
64+
reRenderIframeScript('render-alimentation', 'alimentation', extra)
65+
}
66+
67+
document.addEventListener('DOMContentLoaded', () => {
68+
const base = getBaseUrl()
69+
const search = buildSearch()
70+
71+
buildParamsEditor('params-alimentation', [
72+
{
73+
name: 'alimentationCategory', type: 'select', default: '', options: [
74+
{ value: 'group', label: 'Groupe' },
75+
{ value: 'rayon', label: 'Rayon' },
76+
{ value: 'popularity', label: 'Popularité' },
77+
], hint: 'Catégorie de tri'
78+
},
79+
{ name: 'alimentationEquivalents', type: 'text', default: '', placeholder: 'tomate,carotte,poulet', hint: 'Équivalents personnalisés (slugs séparés par virgule)' },
80+
], (values) => {
81+
renderAlimentation(getExtraSearchFromParams(values))
82+
})
83+
84+
renderAlimentation({})
85+
86+
const directSrc = `${base}/iframes/alimentation${search}${search ? '&' : '?'}source=${encodeURIComponent(window.location.href)}`
87+
renderCodeBlock('code-direct',
88+
`<iframe
89+
src="${directSrc}"
90+
style="border: none; width: 100%; min-height: 500px;"
91+
allowfullscreen></iframe>`)
92+
injectDirectIframe('render-direct', 'alimentation')
93+
94+
renderCodeBlock('code-api',
95+
`fetch('${base}/api/v1/alimentation?category=group')
96+
.then(res => res.json())
97+
.then(data => console.log(data))`)
98+
})
99+
</script>
100+
</body>
101+
102+
</html>

0 commit comments

Comments
 (0)