@i-santos/firestack 1.0.0

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.
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5
+ REGISTRY="${FIRESTACK_REGISTRY:-http://127.0.0.1:4873}"
6
+ NPM_CACHE="${FIRESTACK_NPM_CACHE:-/tmp/npm-cache-firestack}"
7
+
8
+ cd "$ROOT_DIR"
9
+
10
+ if ! npm --cache "$NPM_CACHE" whoami --registry "$REGISTRY" >/dev/null 2>&1; then
11
+ echo "[firestack] npm auth missing for $REGISTRY"
12
+ echo "[firestack] run: npm adduser --registry $REGISTRY"
13
+ exit 1
14
+ fi
15
+
16
+ npm --cache "$NPM_CACHE" publish --registry "$REGISTRY"
@@ -0,0 +1,37 @@
1
+ # Firestack default Docker build context ignore list.
2
+ # Keep package manifests and lockfiles available for deterministic installs.
3
+
4
+ .git
5
+ .github
6
+ .idea
7
+ .vscode
8
+
9
+ out
10
+ coverage
11
+ dist
12
+ build
13
+ tmp
14
+ temp
15
+
16
+ .cache
17
+ .parcel-cache
18
+ .next
19
+ .nuxt
20
+ .svelte-kit
21
+ .turbo
22
+ .nx
23
+
24
+ .firebase
25
+ firebase-debug.log*
26
+
27
+ node_modules
28
+ **/node_modules
29
+ playwright-report
30
+ test-results
31
+
32
+ *.tsbuildinfo
33
+ .DS_Store
34
+ npm-debug.log*
35
+ yarn-debug.log*
36
+ yarn-error.log*
37
+ pnpm-debug.log*
@@ -0,0 +1,4 @@
1
+ # Default app environment
2
+ GCLOUD_PROJECT=demo-your-project
3
+ VITE_USE_FIREBASE_EMULATOR=true
4
+ VITE_FIREBASE_EMULATOR_HOST=127.0.0.1
@@ -0,0 +1,4 @@
1
+ # Development app environment
2
+ GCLOUD_PROJECT=demo-your-project
3
+ VITE_USE_FIREBASE_EMULATOR=true
4
+ VITE_FIREBASE_EMULATOR_HOST=127.0.0.1
@@ -0,0 +1,2 @@
1
+ # Production app environment
2
+ GCLOUD_PROJECT=prod-your-project
@@ -0,0 +1,4 @@
1
+ # Staging app environment
2
+ GCLOUD_PROJECT=staging-your-project
3
+ E2E_BASE_URL=https://staging.example.com
4
+ ALLOW_NON_STAGING_E2E=false
@@ -0,0 +1,6 @@
1
+ # Default test environment
2
+ GCLOUD_PROJECT=demo-your-project
3
+ FIREBASE_AUTH_EMULATOR_HOST=127.0.0.1:9099
4
+ FIRESTORE_EMULATOR_HOST=127.0.0.1:8080
5
+ STRIPE_WEBHOOK_SECRET=whsec_test_123
6
+ STRIPE_SECRET_KEY=sk_test_123
@@ -0,0 +1,6 @@
1
+ # Development test environment
2
+ GCLOUD_PROJECT=demo-your-project
3
+ FIREBASE_AUTH_EMULATOR_HOST=127.0.0.1:9099
4
+ FIRESTORE_EMULATOR_HOST=127.0.0.1:8080
5
+ STRIPE_WEBHOOK_SECRET=whsec_test_123
6
+ STRIPE_SECRET_KEY=sk_test_123
@@ -0,0 +1,4 @@
1
+ # Production test environment
2
+ GCLOUD_PROJECT=prod-your-project
3
+ E2E_BASE_URL=https://app.example.com
4
+ ALLOW_NON_STAGING_E2E=false
@@ -0,0 +1,8 @@
1
+ # Staging test environment
2
+ GCLOUD_PROJECT=staging-your-project
3
+ E2E_BASE_URL=https://staging.example.com
4
+ ALLOW_NON_STAGING_E2E=false
5
+ E2E_STAGING_SMOKE_EMAIL=e2e-smoke@example.com
6
+ E2E_STAGING_SMOKE_PASSWORD=change-me
7
+ E2E_STAGING_FULL_EMAIL=e2e-full@example.com
8
+ E2E_STAGING_FULL_PASSWORD=change-me
@@ -0,0 +1,101 @@
1
+ {
2
+ "version": 1,
3
+ "env": {
4
+ "profiles": {
5
+ "default": {
6
+ "files": [
7
+ {
8
+ "target": ".env.default",
9
+ "template": "env/.env.default.example"
10
+ },
11
+ {
12
+ "target": ".env.test.default",
13
+ "template": "env/.env.test.default.example"
14
+ }
15
+ ]
16
+ },
17
+ "staging": {
18
+ "files": [
19
+ {
20
+ "target": ".env.staging",
21
+ "template": "env/.env.staging.example"
22
+ },
23
+ {
24
+ "target": ".env.test.staging",
25
+ "template": "env/.env.test.staging.example"
26
+ }
27
+ ]
28
+ },
29
+ "production": {
30
+ "files": [
31
+ {
32
+ "target": ".env.production",
33
+ "template": "env/.env.production.example"
34
+ },
35
+ {
36
+ "target": ".env.test.production",
37
+ "template": "env/.env.test.production.example"
38
+ }
39
+ ]
40
+ }
41
+ }
42
+ },
43
+ "test": {
44
+ "commands": {
45
+ "unit": "mkdir -p out/tests/unit && node --test --experimental-strip-types --test-reporter=spec --test-reporter=junit --test-reporter-destination=stdout --test-reporter-destination=out/tests/unit/junit.xml tests/unit/**/*.test.ts",
46
+ "integration": "firestack internal run-functions-build && firebase emulators:exec --project ${GCLOUD_PROJECT:?Set GCLOUD_PROJECT} \"firestack internal run-integration-report\"",
47
+ "e2eSmoke": "mkdir -p out/tests/e2e && rm -f out/tests/e2e/junit.xml && rm -rf out/tests/e2e/html out/tests/e2e/artifacts && firestack internal run-e2e smoke",
48
+ "e2eFull": "mkdir -p out/tests/e2e && rm -f out/tests/e2e/junit.xml && rm -rf out/tests/e2e/html out/tests/e2e/artifacts && firestack internal run-e2e full",
49
+ "stagingSmoke": "mkdir -p out/tests/e2e/staging && rm -f out/tests/e2e/staging/junit.xml && rm -rf out/tests/e2e/staging/html out/tests/e2e/staging/artifacts && firestack internal run-e2e-staging smoke",
50
+ "stagingFull": "mkdir -p out/tests/e2e/staging && rm -f out/tests/e2e/staging/junit.xml && rm -rf out/tests/e2e/staging/html out/tests/e2e/staging/artifacts && firestack internal run-e2e-staging full",
51
+ "ci": "mkdir -p out/tests/unit out/tests/e2e && rm -rf out/tests/e2e/html out/tests/e2e/artifacts && node --test --experimental-strip-types --test-reporter=spec --test-reporter=junit --test-reporter-destination=stdout --test-reporter-destination=out/tests/unit/junit.xml tests/unit/**/*.test.ts && firestack internal run-functions-build && firebase emulators:exec --project ${GCLOUD_PROJECT:?Set GCLOUD_PROJECT} \"bash -lc 'set +e; firestack internal run-integration-report; integration_status=\\$?; firestack internal run-e2e smoke; e2e_status=\\$?; set -e; if [ \\$integration_status -ne 0 ] || [ \\$e2e_status -ne 0 ]; then exit 1; fi'\"",
52
+ "ciFailFast": "mkdir -p out/tests/unit out/tests/e2e && rm -rf out/tests/e2e/html out/tests/e2e/artifacts && node --test --experimental-strip-types --test-reporter=spec --test-reporter=junit --test-reporter-destination=stdout --test-reporter-destination=out/tests/unit/junit.xml tests/unit/**/*.test.ts && firestack internal run-functions-build && firebase emulators:exec --project ${GCLOUD_PROJECT:?Set GCLOUD_PROJECT} \"firestack internal run-integration-report && firestack internal run-e2e smoke\""
53
+ },
54
+ "docker": {
55
+ "dockerfile": "tests/Dockerfile",
56
+ "imageBaseName": "firestack-tests",
57
+ "nodeModulesVolumePrefix": "firestack-node_modules-",
58
+ "functionsNodeModulesVolumePrefix": "firestack-functions-node_modules-",
59
+ "emulatorCacheVolumePrefix": "firestack-firebase-cache-",
60
+ "buildNetwork": "host",
61
+ "workdir": "/work",
62
+ "runAsHostUser": true,
63
+ "preloadFirestoreEmulator": true,
64
+ "writablePaths": [
65
+ "out"
66
+ ],
67
+ "bootstrapCommand": "if [ ! -d /work/node_modules/.bin ]; then mkdir -p /work/node_modules && cp -a /opt/deps/node_modules/. /work/node_modules/; fi && if [ -n \"${FIRESTACK_FUNCTIONS_PATHS:-}\" ]; then IFS=',' read -r -a firestack_functions <<< \"$FIRESTACK_FUNCTIONS_PATHS\"; for rel in \"${firestack_functions[@]}\"; do if [ -n \"$rel\" ] && [ -d \"/opt/deps/$rel/node_modules\" ] && [ -f \"/work/$rel/package.json\" ] && [ ! -d \"/work/$rel/node_modules/.bin\" ]; then mkdir -p \"/work/$rel/node_modules\" && cp -a \"/opt/deps/$rel/node_modules/.\" \"/work/$rel/node_modules/\"; fi; done; fi",
68
+ "cleanup": true,
69
+ "addHosts": [
70
+ "host.docker.internal:host-gateway"
71
+ ],
72
+ "passThroughEnv": [
73
+ "GCLOUD_PROJECT",
74
+ "FIREBASE_PROJECT_ALIAS",
75
+ "ALLOW_NON_STAGING_E2E",
76
+ "E2E_BASE_URL",
77
+ "E2E_CLEANUP",
78
+ "E2E_RUN_ID",
79
+ "E2E_EMAIL",
80
+ "E2E_PASSWORD",
81
+ "E2E_STAGING_EMAIL",
82
+ "E2E_STAGING_PASSWORD",
83
+ "E2E_STAGING_SMOKE_EMAIL",
84
+ "E2E_STAGING_SMOKE_PASSWORD",
85
+ "E2E_STAGING_FULL_EMAIL",
86
+ "E2E_STAGING_FULL_PASSWORD",
87
+ "E2E_APP_CHECK_DEBUG_TOKEN",
88
+ "FIREBASE_AUTH_EMULATOR_HOST",
89
+ "FIRESTORE_EMULATOR_HOST",
90
+ "VITE_USE_FIREBASE_EMULATOR",
91
+ "VITE_FIREBASE_EMULATOR_HOST",
92
+ "STRIPE_WEBHOOK_SECRET",
93
+ "STRIPE_SECRET_KEY"
94
+ ],
95
+ "registry": {
96
+ "defaultHostUrl": "http://127.0.0.1:4873",
97
+ "defaultDockerUrl": "http://host.docker.internal:4873"
98
+ }
99
+ }
100
+ }
101
+ }
@@ -0,0 +1,65 @@
1
+ import { defineConfig, devices } from '@playwright/test';
2
+
3
+ const baseURL = process.env.E2E_BASE_URL ?? 'http://127.0.0.1:5173';
4
+
5
+ function normalizeHost(rawUrl) {
6
+ try {
7
+ return new URL(rawUrl).hostname.toLowerCase();
8
+ } catch {
9
+ return null;
10
+ }
11
+ }
12
+
13
+ export function resolvePlaywrightOutputBaseDir(env = process.env) {
14
+ const explicitJunit = env.PLAYWRIGHT_JUNIT_OUTPUT_FILE?.trim();
15
+ const explicitHtml = env.PLAYWRIGHT_HTML_OUTPUT_DIR?.trim();
16
+ const explicitArtifacts = env.PLAYWRIGHT_OUTPUT_DIR?.trim();
17
+ if (explicitJunit || explicitHtml || explicitArtifacts) {
18
+ return 'out/tests/e2e';
19
+ }
20
+
21
+ const profileAlias = env.FIREBASE_PROJECT_ALIAS?.trim().toLowerCase();
22
+ if (profileAlias === 'staging') {
23
+ return 'out/tests/e2e/staging';
24
+ }
25
+
26
+ const host = normalizeHost(env.E2E_BASE_URL?.trim());
27
+ if (host === 'staging.presentgoal.com') {
28
+ return 'out/tests/e2e/staging';
29
+ }
30
+
31
+ return 'out/tests/e2e';
32
+ }
33
+
34
+ const baseOutputDir = resolvePlaywrightOutputBaseDir(process.env);
35
+ const junitOutputFile = process.env.PLAYWRIGHT_JUNIT_OUTPUT_FILE ?? `${baseOutputDir}/junit.xml`;
36
+ const htmlOutputFolder = process.env.PLAYWRIGHT_HTML_OUTPUT_DIR ?? `${baseOutputDir}/html`;
37
+ const outputDir = process.env.PLAYWRIGHT_OUTPUT_DIR ?? `${baseOutputDir}/artifacts`;
38
+
39
+ export default defineConfig({
40
+ testDir: 'tests/e2e',
41
+ outputDir,
42
+ timeout: 30_000,
43
+ expect: {
44
+ timeout: 5_000,
45
+ },
46
+ fullyParallel: true,
47
+ retries: process.env.CI ? 1 : 0,
48
+ reporter: [
49
+ ['list'],
50
+ ['html', { outputFolder: htmlOutputFolder, open: 'never' }],
51
+ ['junit', { outputFile: junitOutputFile }],
52
+ ],
53
+ use: {
54
+ baseURL,
55
+ trace: 'retain-on-failure',
56
+ video: 'retain-on-failure',
57
+ screenshot: 'only-on-failure',
58
+ },
59
+ projects: [
60
+ {
61
+ name: 'chromium',
62
+ use: { ...devices['Desktop Chrome'] },
63
+ },
64
+ ],
65
+ });
@@ -0,0 +1,43 @@
1
+ FROM mcr.microsoft.com/playwright:v1.58.2-noble AS base
2
+ FROM eclipse-temurin:21-jre AS jre21
3
+ FROM base
4
+
5
+ USER root
6
+
7
+ ARG FIREBASE_CONFIG_PATH=firebase.json
8
+ ARG FIRESTACK_FUNCTIONS_INSTALL_PATHS=
9
+
10
+ COPY --from=jre21 /opt/java/openjdk /opt/java/openjdk
11
+ ENV JAVA_HOME=/opt/java/openjdk
12
+ ENV PATH="${JAVA_HOME}/bin:${PATH}"
13
+
14
+ WORKDIR /opt/deps
15
+
16
+ COPY package*.json ./
17
+
18
+ RUN --mount=type=cache,target=/root/.npm \
19
+ if [ -f package.json ]; then (npm ci || npm install); fi
20
+
21
+ RUN npm install -g firebase-tools \
22
+ && mkdir -p /opt/firebase/emulators \
23
+ && FIREBASE_EMULATORS_PATH=/opt/firebase/emulators firebase setup:emulators:firestore
24
+
25
+ COPY . .
26
+
27
+ RUN --mount=type=cache,target=/root/.npm \
28
+ if [ -n "$FIRESTACK_FUNCTIONS_INSTALL_PATHS" ]; then \
29
+ printf '%s' "$FIRESTACK_FUNCTIONS_INSTALL_PATHS" | tr ',' '\n' > /tmp/firestack-functions-paths; \
30
+ while IFS= read -r rel || [ -n "$rel" ]; do \
31
+ [ -z "$rel" ] && continue; \
32
+ [ ! -f "$rel/package.json" ] && continue; \
33
+ (cd "$rel" && (npm ci || npm install)); \
34
+ done < /tmp/firestack-functions-paths; \
35
+ elif [ -f "$FIREBASE_CONFIG_PATH" ]; then \
36
+ FIREBASE_CONFIG_PATH="$FIREBASE_CONFIG_PATH" node -e "const fs=require('fs');const p=process.env.FIREBASE_CONFIG_PATH||'firebase.json';const cfg=JSON.parse(fs.readFileSync(p,'utf8'));const found=[];const add=(v)=>{if(typeof v!=='string')return;const n=v.trim().replace(/\\\\/g,'/').replace(/^\\.\\//,'');if(!n||n.startsWith('/')||n.includes('..'))return;if(fs.existsSync(n+'/package.json'))found.push(n);};const f=cfg.functions;if(typeof f==='string')add(f);else if(Array.isArray(f)){for(const e of f){if(typeof e==='string')add(e);else if(e&&typeof e==='object')add(e.source);}}else if(f&&typeof f==='object')add(f.source);process.stdout.write([...new Set(found)].join('\n'));" >/tmp/firestack-functions-paths; \
37
+ while IFS= read -r rel || [ -n "$rel" ]; do \
38
+ [ -z "$rel" ] && continue; \
39
+ (cd "$rel" && (npm ci || npm install)); \
40
+ done < /tmp/firestack-functions-paths; \
41
+ elif [ -f functions/package.json ]; then (cd functions && (npm ci || npm install)); fi
42
+
43
+ WORKDIR /work