Skip to content

Commit 4ff8e80

Browse files
committed
Create E2E app
1 parent 466612a commit 4ff8e80

File tree

94 files changed

+11459
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+11459
-0
lines changed

test_apps/e2e-app/.editorconfig

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# editorconfig.org
2+
3+
root = true
4+
5+
[*]
6+
charset = utf-8
7+
end_of_line = lf
8+
indent_size = 4
9+
indent_style = space
10+
insert_final_newline = true
11+
trim_trailing_whitespace = true
12+
13+
[{compose.yaml,compose.*.yaml}]
14+
indent_size = 2
15+
16+
[*.md]
17+
trim_trailing_whitespace = false

test_apps/e2e-app/.env

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# In all environments, the following files are loaded if they exist,
2+
# the latter taking precedence over the former:
3+
#
4+
# * .env contains default values for the environment variables needed by the app
5+
# * .env.local uncommitted file with local overrides
6+
# * .env.$APP_ENV committed environment-specific defaults
7+
# * .env.$APP_ENV.local uncommitted environment-specific overrides
8+
#
9+
# Real environment variables win over .env files.
10+
#
11+
# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES.
12+
# https://symfony.com/doc/current/configuration/secrets.html
13+
#
14+
# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2).
15+
# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration
16+
17+
###> symfony/framework-bundle ###
18+
APP_ENV=dev
19+
APP_SECRET=
20+
###< symfony/framework-bundle ###
21+
22+
###> doctrine/doctrine-bundle ###
23+
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
24+
# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
25+
#
26+
DATABASE_URL="sqlite:///%kernel.project_dir%/var/data_%kernel.environment%.db"
27+
# DATABASE_URL="mysql://app:[email protected]:3306/app?serverVersion=8.0.32&charset=utf8mb4"
28+
# DATABASE_URL="mysql://app:[email protected]:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4"
29+
# DATABASE_URL="postgresql://app:[email protected]:5432/app?serverVersion=16&charset=utf8"
30+
###< doctrine/doctrine-bundle ###
31+
32+
###> symfony/ux-google-map ###
33+
# Options available at https://github.com/symfony/ux/blob/2.x/src/Map/src/Bridge/Google/README.md
34+
#
35+
GOOGLE_MAPS_API_KEY="# Get your API key at https://developers.google.com/maps/documentation/javascript/get-api-key"
36+
#UX_MAP_DSN=google://%env(GOOGLE_MAPS_API_KEY)%@default
37+
###< symfony/ux-google-map ###
38+
39+
###> symfony/ux-leaflet-map ###
40+
# Options available at https://github.com/symfony/ux/blob/2.x/src/Map/src/Bridge/Leaflet/README.md
41+
#
42+
UX_MAP_DSN=leaflet://default
43+
###< symfony/ux-leaflet-map ###
44+
45+
###> symfony/mercure-notifier ###
46+
# MERCURE_DSN=mercure://default
47+
###< symfony/mercure-notifier ###
48+
49+
###> symfony/mercure-bundle ###
50+
# See https://symfony.com/doc/current/mercure.html#configuration
51+
# The URL of the Mercure hub, used by the app to publish updates (can be a local URL)
52+
MERCURE_URL=https://example.com/.well-known/mercure
53+
# The public URL of the Mercure hub, used by the browser to connect
54+
MERCURE_PUBLIC_URL=https://example.com/.well-known/mercure
55+
# The secret used to sign the JWTs
56+
MERCURE_JWT_SECRET="!ChangeThisMercureHubJWTSecretKey!"
57+
###< symfony/mercure-bundle ###

test_apps/e2e-app/.env.dev

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
###> symfony/framework-bundle ###
3+
APP_SECRET=6714654acdda58a8d804a0d5e10f98a2
4+
###< symfony/framework-bundle ###

test_apps/e2e-app/.gitignore

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
###> symfony/framework-bundle ###
2+
/.env.local
3+
/.env.local.php
4+
/.env.*.local
5+
/config/secrets/prod/prod.decrypt.private.php
6+
/public/bundles/
7+
/var/
8+
/vendor/
9+
###< symfony/framework-bundle ###
10+
11+
###> symfony/asset-mapper ###
12+
/public/assets/
13+
/assets/vendor/
14+
###< symfony/asset-mapper ###
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
http:
2+
port: 9876

test_apps/e2e-app/README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# E2E App
2+
3+
This is a Symfony application designed for end-to-end testing.
4+
5+
It serves for testing UX packages in a real-world scenario,
6+
to ensure they work as expected for multiple Symfony versions and various browsers.
7+
8+
## Requirements
9+
10+
- Symfony CLI
11+
- PHP 8.1 or higher
12+
- Docker and Docker Compose
13+
- Composer
14+
15+
## Installation
16+
17+
```shell
18+
docker compose up -d
19+
symfony php ../.github/build-packages.php
20+
21+
SYMFONY_REQUIRE=6.4.* symfony composer update
22+
# or...
23+
SYMFONY_REQUIRE=7.3.* symfony composer update
24+
```
25+
26+
## Usage
27+
28+
```shell
29+
symfony serve
30+
```
31+
32+
The application will be available at `http://localhost:9876`.

test_apps/e2e-app/assets/app.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { registerVueControllerComponents } from '@symfony/ux-vue';
2+
import { registerSvelteControllerComponents } from '@symfony/ux-svelte';
3+
import { registerReactControllerComponents } from '@symfony/ux-react';
4+
import './bootstrap.js';
5+
/*
6+
* Welcome to your app's main JavaScript file!
7+
*
8+
* This file will be included onto the page via the importmap() Twig function,
9+
* which should already be in your base.html.twig.
10+
*/
11+
import 'bootstrap/dist/css/bootstrap.min.css';
12+
import './styles/app.css';
13+
14+
console.log('This log comes from assets/app.js - welcome to AssetMapper! 🎉');
15+
16+
registerReactControllerComponents();
17+
registerSvelteControllerComponents();
18+
registerVueControllerComponents();
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { startStimulusApp } from '@symfony/stimulus-bundle';
2+
3+
const app = startStimulusApp();
4+
// register any custom, 3rd party controllers here
5+
// app.register('some_controller_name', SomeImportedController);
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
{
2+
"controllers": {
3+
"@symfony/ux-autocomplete": {
4+
"autocomplete": {
5+
"enabled": true,
6+
"fetch": "eager",
7+
"autoimport": {
8+
"tom-select/dist/css/tom-select.default.css": true,
9+
"tom-select/dist/css/tom-select.bootstrap4.css": false,
10+
"tom-select/dist/css/tom-select.bootstrap5.css": false
11+
}
12+
}
13+
},
14+
"@symfony/ux-chartjs": {
15+
"chart": {
16+
"enabled": true,
17+
"fetch": "eager"
18+
}
19+
},
20+
"@symfony/ux-cropperjs": {
21+
"cropper": {
22+
"enabled": true,
23+
"fetch": "eager",
24+
"autoimport": {
25+
"cropperjs/dist/cropper.min.css": true,
26+
"@symfony/ux-cropperjs/dist/style.min.css": true
27+
}
28+
}
29+
},
30+
"@symfony/ux-dropzone": {
31+
"dropzone": {
32+
"enabled": true,
33+
"fetch": "eager",
34+
"autoimport": {
35+
"@symfony/ux-dropzone/dist/style.min.css": true
36+
}
37+
}
38+
},
39+
"@symfony/ux-google-map": {
40+
"map": {
41+
"enabled": true,
42+
"fetch": "lazy"
43+
}
44+
},
45+
"@symfony/ux-lazy-image": {
46+
"lazy-image": {
47+
"enabled": true,
48+
"fetch": "eager"
49+
}
50+
},
51+
"@symfony/ux-leaflet-map": {
52+
"map": {
53+
"enabled": true,
54+
"fetch": "lazy"
55+
}
56+
},
57+
"@symfony/ux-live-component": {
58+
"live": {
59+
"enabled": true,
60+
"fetch": "eager",
61+
"autoimport": {
62+
"@symfony/ux-live-component/dist/live.min.css": true
63+
}
64+
}
65+
},
66+
"@symfony/ux-notify": {
67+
"notify": {
68+
"enabled": true,
69+
"fetch": "eager"
70+
}
71+
},
72+
"@symfony/ux-react": {
73+
"react": {
74+
"enabled": true,
75+
"fetch": "eager"
76+
}
77+
},
78+
"@symfony/ux-svelte": {
79+
"svelte": {
80+
"enabled": true,
81+
"fetch": "eager"
82+
}
83+
},
84+
"@symfony/ux-swup": {
85+
"swup": {
86+
"enabled": true,
87+
"fetch": "eager"
88+
}
89+
},
90+
"@symfony/ux-toggle-password": {
91+
"toggle-password": {
92+
"enabled": true,
93+
"fetch": "eager",
94+
"autoimport": {
95+
"@symfony/ux-toggle-password/dist/style.min.css": true
96+
}
97+
}
98+
},
99+
"@symfony/ux-turbo": {
100+
"turbo-core": {
101+
"enabled": true,
102+
"fetch": "eager"
103+
},
104+
"mercure-turbo-stream": {
105+
"enabled": false,
106+
"fetch": "eager"
107+
}
108+
},
109+
"@symfony/ux-typed": {
110+
"typed": {
111+
"enabled": true,
112+
"fetch": "eager"
113+
}
114+
},
115+
"@symfony/ux-vue": {
116+
"vue": {
117+
"enabled": true,
118+
"fetch": "eager"
119+
}
120+
}
121+
},
122+
"entrypoints": []
123+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
const nameCheck = /^[-_a-zA-Z0-9]{4,22}$/;
2+
const tokenCheck = /^[-_/+a-zA-Z0-9]{24,}$/;
3+
4+
// Generate and double-submit a CSRF token in a form field and a cookie, as defined by Symfony's SameOriginCsrfTokenManager
5+
document.addEventListener('submit', function (event) {
6+
generateCsrfToken(event.target);
7+
}, true);
8+
9+
// When @hotwired/turbo handles form submissions, send the CSRF token in a header in addition to a cookie
10+
// The `framework.csrf_protection.check_header` config option needs to be enabled for the header to be checked
11+
document.addEventListener('turbo:submit-start', function (event) {
12+
const h = generateCsrfHeaders(event.detail.formSubmission.formElement);
13+
Object.keys(h).map(function (k) {
14+
event.detail.formSubmission.fetchRequest.headers[k] = h[k];
15+
});
16+
});
17+
18+
// When @hotwired/turbo handles form submissions, remove the CSRF cookie once a form has been submitted
19+
document.addEventListener('turbo:submit-end', function (event) {
20+
removeCsrfToken(event.detail.formSubmission.formElement);
21+
});
22+
23+
export function generateCsrfToken (formElement) {
24+
const csrfField = formElement.querySelector('input[data-controller="csrf-protection"], input[name="_csrf_token"]');
25+
26+
if (!csrfField) {
27+
return;
28+
}
29+
30+
let csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value');
31+
let csrfToken = csrfField.value;
32+
33+
if (!csrfCookie && nameCheck.test(csrfToken)) {
34+
csrfField.setAttribute('data-csrf-protection-cookie-value', csrfCookie = csrfToken);
35+
csrfField.defaultValue = csrfToken = btoa(String.fromCharCode.apply(null, (window.crypto || window.msCrypto).getRandomValues(new Uint8Array(18))));
36+
csrfField.dispatchEvent(new Event('change', { bubbles: true }));
37+
}
38+
39+
if (csrfCookie && tokenCheck.test(csrfToken)) {
40+
const cookie = csrfCookie + '_' + csrfToken + '=' + csrfCookie + '; path=/; samesite=strict';
41+
document.cookie = window.location.protocol === 'https:' ? '__Host-' + cookie + '; secure' : cookie;
42+
}
43+
}
44+
45+
export function generateCsrfHeaders (formElement) {
46+
const headers = {};
47+
const csrfField = formElement.querySelector('input[data-controller="csrf-protection"], input[name="_csrf_token"]');
48+
49+
if (!csrfField) {
50+
return headers;
51+
}
52+
53+
const csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value');
54+
55+
if (tokenCheck.test(csrfField.value) && nameCheck.test(csrfCookie)) {
56+
headers[csrfCookie] = csrfField.value;
57+
}
58+
59+
return headers;
60+
}
61+
62+
export function removeCsrfToken (formElement) {
63+
const csrfField = formElement.querySelector('input[data-controller="csrf-protection"], input[name="_csrf_token"]');
64+
65+
if (!csrfField) {
66+
return;
67+
}
68+
69+
const csrfCookie = csrfField.getAttribute('data-csrf-protection-cookie-value');
70+
71+
if (tokenCheck.test(csrfField.value) && nameCheck.test(csrfCookie)) {
72+
const cookie = csrfCookie + '_' + csrfField.value + '=0; path=/; samesite=strict; max-age=0';
73+
74+
document.cookie = window.location.protocol === 'https:' ? '__Host-' + cookie + '; secure' : cookie;
75+
}
76+
}
77+
78+
/* stimulusFetch: 'lazy' */
79+
export default 'csrf-protection-controller';

0 commit comments

Comments
 (0)