}
- */
-const svelteKit = async ({body, headers, method, rawBody, url}, response) => {
- const host = `${headers['x-forwarded-proto']}://${headers.host}`;
- const {pathname, searchParams: searchParameters = ''} = new URL(url || '', host);
-
- const finalRawBody =
- headers['content-type'] === undefined ?
- rawBody :
- (headers['content-type'] === 'application/octet-stream' ?
- body :
- new TextDecoder(headers['content-encoding'] || 'utf-8').decode(rawBody));
-
- const rendered = await render({
- method,
- headers,
- path: pathname,
- query: searchParameters,
- rawBody: finalRawBody
- });
-
- if (rendered) {
- const {status, headers, body} = rendered;
- return response.writeHead(status, headers).end(body);
- }
-
- return response.writeHead(404).end();
-};
-
-export default svelteKit;
diff --git a/src/files/shims.js b/src/files/shims.js
index 29d9c09..0f3dffe 100644
--- a/src/files/shims.js
+++ b/src/files/shims.js
@@ -1 +1,2 @@
+// @ts-expect-error
export {fetch, Response, Request, Headers} from '@sveltejs/kit/install-fetch';
diff --git a/src/index.js b/src/index.js
index 759e63a..1f2ab88 100644
--- a/src/index.js
+++ b/src/index.js
@@ -102,9 +102,9 @@ async function adaptToCloudFunctions({utils, esbuildBuildOptions, name, source,
let ${ssrSvelteFunctionName};
exports.${name} = functions.https.onRequest(async (request, response) => {
if (!${ssrSvelteFunctionName}) {
- functions.logger.info("Initialising SvelteKit SSR Handler");
+ functions.logger.info("Initialising SvelteKit SSR entry");
${ssrSvelteFunctionName} = require("./${ssrDirname}/index").default;
- functions.logger.info("SvelteKit SSR Handler initialised!");
+ functions.logger.info("SvelteKit SSR entry initialised!");
}
functions.logger.info("Requested resource: " + request.originalUrl);
return ${ssrSvelteFunctionName}(request, response);
@@ -141,7 +141,7 @@ async function adaptToCloudRun({utils, esbuildBuildOptions, serviceId, region, f
pkgjson.dependencies = {};
}
- pkgjson.dependencies['@google-cloud/functions-framework'] = '^1.7.1';
+ pkgjson.dependencies['@google-cloud/functions-framework'] = '^1.9.0';
pkgjson.engines = {node: '14'};
delete pkgjson.type;
const data = JSON.stringify(pkgjson, null, 2);
@@ -173,13 +173,16 @@ async function prepareEntrypoint({utils, esbuildBuildOptions, serverOutputDir})
utils.rimraf(serverOutputDir);
const files = fileURLToPath(new URL('./files', import.meta.url));
- const handlerSource = path.join(files, 'handler.js');
- const handlerDest = path.join(temporaryDir, 'handler.js');
- utils.copy(handlerSource, handlerDest);
+ const entrySource = path.join(files, 'entry.js');
+ const entryDest = path.join(temporaryDir, 'entry.js');
+ utils.copy(entrySource, entryDest);
+ const firebaseToSvelteKitSource = path.join(files, 'firebase-to-svelte-kit.js');
+ const firebaseToSvelteKitDest = path.join(temporaryDir, 'firebase-to-svelte-kit.js');
+ utils.copy(firebaseToSvelteKitSource, firebaseToSvelteKitDest);
/** @type {BuildOptions} */
const defaultOptions = {
- entryPoints: [path.join(temporaryDir, 'handler.js')],
+ entryPoints: [path.join(temporaryDir, 'entry.js')],
outfile: path.join(serverOutputDir, 'index.js'),
bundle: true,
inject: [path.join(files, 'shims.js')],
diff --git a/tests/end-to-end/scaffold/.firebaserc b/tests/end-to-end/scaffold/.firebaserc
new file mode 100644
index 0000000..d8085fd
--- /dev/null
+++ b/tests/end-to-end/scaffold/.firebaserc
@@ -0,0 +1 @@
+{"projects":{"default":"demo"}}
diff --git a/examples/functions_single_site/firebase.json b/tests/end-to-end/scaffold/firebase.json
similarity index 69%
rename from examples/functions_single_site/firebase.json
rename to tests/end-to-end/scaffold/firebase.json
index 79da104..839c82b 100644
--- a/examples/functions_single_site/firebase.json
+++ b/tests/end-to-end/scaffold/firebase.json
@@ -14,19 +14,13 @@
},
"emulators": {
"ui": {
- "enabled": true
+ "enabled": false
+ },
+ "hosting": {
+ "port": 8685
},
"functions": {
"port": 5001
- },
- "auth": {
- "port": 9099
- },
- "firestore": {
- "port": 8080
- },
- "pubsub": {
- "port": 8085
}
}
}
diff --git a/examples/functions_single_site/functions/.gitignore b/tests/end-to-end/scaffold/functions/.gitignore
similarity index 100%
rename from examples/functions_single_site/functions/.gitignore
rename to tests/end-to-end/scaffold/functions/.gitignore
diff --git a/examples/functions_single_site/functions/index.js b/tests/end-to-end/scaffold/functions/index.js
similarity index 100%
rename from examples/functions_single_site/functions/index.js
rename to tests/end-to-end/scaffold/functions/index.js
diff --git a/examples/functions_single_site/functions/package.json b/tests/end-to-end/scaffold/functions/package.json
similarity index 100%
rename from examples/functions_single_site/functions/package.json
rename to tests/end-to-end/scaffold/functions/package.json
diff --git a/examples/functions_single_site/svelte.config.js b/tests/end-to-end/scaffold/svelte.config.js
similarity index 100%
rename from examples/functions_single_site/svelte.config.js
rename to tests/end-to-end/scaffold/svelte.config.js
diff --git a/tests/end-to-end/test.bash b/tests/end-to-end/test.bash
new file mode 100755
index 0000000..687b2ad
--- /dev/null
+++ b/tests/end-to-end/test.bash
@@ -0,0 +1,113 @@
+#!/usr/bin/env bash
+
+# undefined vars are errors
+set -u
+IFS=$'\n\t'
+
+# Execute end-to-end tests of the SvelteKit Todo template app built with
+# the svelte-adapter-firebase adapter hosted on the Cloud Function using
+# the Firebase Emulator in CI
+#
+# Curl API and assert response payload
+#
+# Usage:
+#
+# tests/integration/test.bash
+
+SCRIPT_PATH=$(dirname "$(realpath -s "$0")")
+TEST_DIR="$(mktemp -dt svelte-adapter-firebase-XXXX)"
+PORT="8685" # from test-firebase.json
+INDICATOR="====> "
+
+# Cleanup files on exit
+trap 'echo "${INDICATOR}Exiting, removing ${TEST_DIR} & killing all processes matching _firebase_" && rm -rf -- "$TEST_DIR" && pkill -f firebase' EXIT
+
+echo "TEST_DIR: ${TEST_DIR}"
+echo "PWD: ${PWD}"
+
+echo "${INDICATOR}Install svelte-adapter-firebase ${SCRIPT_PATH}/../../ deps"
+npm install
+
+echo "${INDICATOR}init SvelteKit Todos app"
+yes "" | "$(npm init svelte@next "${TEST_DIR}")"
+echo "${INDICATOR}Complete SvelteKit init"
+
+cp -R "${SCRIPT_PATH}"/scaffold/* "${TEST_DIR}"
+cp "${SCRIPT_PATH}/scaffold/.firebaserc" "${TEST_DIR}/.firebaserc"
+cp ".tool-versions" "${TEST_DIR}/.tool-versions"
+
+cd "${TEST_DIR}" || exit 1
+echo "${INDICATOR}PWD after cd to TEST_DIR: ${PWD}"
+
+echo "${INDICATOR}Set package.json:scripts.build to verbose mode"
+sed -i -e 's/svelte-kit build/svelte-kit build --verbose/g' "${TEST_DIR}/package.json"
+
+echo "${INDICATOR}Install kit template deps"
+npm install
+
+echo "${INDICATOR}Install svelte-adapter-firebase from ${SCRIPT_PATH}/../"
+npm install "${SCRIPT_PATH}/../../"
+
+echo "${INDICATOR}Install functions/ deps"
+npm --prefix functions install
+
+echo "${INDICATOR}Build Kit todos site"
+npm run build
+
+echo "${INDICATOR}Starting emulator"
+firebase emulators:start --only functions,hosting &
+
+sleep 8
+
+echo "${INDICATOR}Test GET static page '/about'"
+EXPECTED_SUBSTRING="The page you're looking at is purely static HTML"
+RESULT="$(curl -L localhost:${PORT}/about)"
+
+if [[ "${RESULT}" != *"${EXPECTED_SUBSTRING}"* ]]; then
+ echo "Failed testing localhost:${PORT}/about"
+ exit 1
+fi
+
+echo "${INDICATOR}Test GET SSR route '/'"
+EXPECTED_SUBSTRING="try editing src/routes/index.svelte
"
+RESULT="$(curl -L localhost:${PORT}/)"
+
+if [[ "${RESULT}" != *"${EXPECTED_SUBSTRING}"* ]]; then
+ echo "${INDICATOR}Failed testing localhost:${PORT}/"
+ exit 1
+fi
+
+echo "${INDICATOR}Test GET SSR route '/todos'"
+EXPECTED_SUBSTRING="Todos
"
+RESULT="$(curl -L localhost:${PORT}/todos)"
+
+if [[ "${RESULT}" != *"${EXPECTED_SUBSTRING}"* ]]; then
+ echo "${INDICATOR}Failed testing localhost:${PORT}/todos/"
+ exit 1
+fi
+
+echo "${INDICATOR}Test POST to '/todos' API"
+EXPECTED_SUBSTRING='"text":"asdf"'
+# expected result = {"uid":"","created_at":01234,"text":"asdf","done":false}
+# generated from the browser & copied with 'copy for cURL' browser context menu
+RESULT="$(curl "http://localhost:${PORT}/todos.json" \
+ -H "User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:91.0) Gecko/20100101 Firefox/91.0" \
+ -H "Accept: application/json" \
+ -H "Accept-Language: en-GB,en;q=0.5" \
+ --compressed \
+ -H "Referer: http://localhost:${PORT}/todos" \
+ -H "Content-Type: multipart/form-data; boundary=---------------------------349341627025106406523834848301" \
+ -H "Origin: http://localhost:${PORT}" \
+ -H "Connection: keep-alive" \
+ -H "Cookie: userid=0a52e7d5-25d4-4b12-b307-38756d00bbcb" \
+ -H "Sec-Fetch-Dest: empty" \
+ -H "Sec-Fetch-Mode: cors" \
+ -H "Sec-Fetch-Site: same-origin" \
+ -H 'Sec-GPC: 1' --data-binary $'-----------------------------349341627025106406523834848301\r\nContent-Disposition: form-data; name="text"\r\n\r\nasdf\r\n-----------------------------349341627025106406523834848301--\r\n')"
+echo "$RESULT"
+if [[ "${RESULT}" != *"${EXPECTED_SUBSTRING}"* ]]; then
+ echo "${INDICATOR}Failed POSTing to localhost:${PORT}/todos.json"
+ exit 1
+fi
+
+echo "${INDICATOR}Success"
diff --git a/tests/index.test.js b/tests/index.test.js
deleted file mode 100644
index 1d16fa7..0000000
--- a/tests/index.test.js
+++ /dev/null
@@ -1,295 +0,0 @@
-import {fileURLToPath} from 'url';
-import path from 'path';
-import test from 'ava';
-import {ensureCompatibleCloudFunctionVersion, ensureStaticResourceDirsDiffer, parseFirebaseConfiguration, validCloudFunctionName, validCloudRunServiceId} from '../src/utils.js';
-
-// ParseFirebaseConfiguration: Valid configs
-test(
- 'Firebase config w Cloud Functions & single site',
- t => {
- const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson: fileURLToPath(new URL('./fixtures/successes/cf_site.json', import.meta.url))};
- const result = parseFirebaseConfiguration(config);
- const expectedResult = {functions: {name: 'some_func', source: path.join(path.dirname(config.firebaseJson), 'functions'), runtime: undefined}, cloudRun: false, publicDir: path.join(path.dirname(config.firebaseJson), 'app'), firebaseJsonDir: path.dirname(config.firebaseJson)};
-
- t.deepEqual(result, expectedResult);
- }
-);
-
-test(
- 'Firebase config w Cloud Functions & multiple sites',
- t => {
- const config = {hostingSite: 'app', sourceRewriteMatch: '**', firebaseJson: fileURLToPath(new URL('./fixtures/successes/cf_sites.json', import.meta.url))};
- const result = parseFirebaseConfiguration(config);
- const expectedResult = {functions: {name: 'some_func', source: path.join(path.dirname(config.firebaseJson), 'functions'), runtime: undefined}, cloudRun: false, publicDir: path.join(path.dirname(config.firebaseJson), 'app'), firebaseJsonDir: path.dirname(config.firebaseJson)};
-
- t.deepEqual(result, expectedResult);
- }
-);
-
-test(
- 'Firebase config w Cloud Run & single site',
- t => {
- const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson: fileURLToPath(new URL('./fixtures/successes/cr_site.json', import.meta.url))};
- const result = parseFirebaseConfiguration(config);
- const expectedResult = {functions: false, cloudRun: {serviceId: 'some-service', region: 'us-central1'}, publicDir: path.join(path.dirname(config.firebaseJson), 'app'), firebaseJsonDir: path.dirname(config.firebaseJson)};
-
- t.deepEqual(result, expectedResult);
- }
-);
-
-test(
- 'Firebase config w Cloud Run & multiple sites',
- t => {
- const config = {hostingSite: 'app', sourceRewriteMatch: '**', firebaseJson: fileURLToPath(new URL('./fixtures/successes/cr_sites.json', import.meta.url))};
- const result = parseFirebaseConfiguration(config);
- const expectedResult = {functions: false, cloudRun: {serviceId: 'some-service', region: 'us-central1'}, publicDir: path.join(path.dirname(config.firebaseJson), 'app'), firebaseJsonDir: path.dirname(config.firebaseJson)};
-
- t.deepEqual(result, expectedResult);
- }
-);
-
-// ParseFirebaseConfiguration: Invalid configs
-test(
- 'Firebase config does not exist',
- t => {
- const firebaseJson = fileURLToPath(new URL('./does_not_exist.json', import.meta.url));
- const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson};
- const error = t.throws(() => parseFirebaseConfiguration(config));
- t.is(error.message, 'See above output. See Hint code SAF1000 in README');
- }
-);
-
-test(
- 'Firebase config is invalid json',
- t => {
- const firebaseJson = fileURLToPath(new URL('./fixtures/failures/invalid.json', import.meta.url));
- const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson};
- const error = t.throws(() => parseFirebaseConfiguration(config));
- t.is(error.message, 'See above output. See Hint code SAF1001 in README');
- }
-);
-
-test(
- 'Firebase config without "hosting" field',
- t => {
- const firebaseJson = fileURLToPath(new URL('./fixtures/failures/missing_hosting.json', import.meta.url));
- const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson};
- const error = t.throws(() => parseFirebaseConfiguration(config));
- t.is(error.message, 'See above output. See Hint code SAF1010 in README');
- }
-);
-
-test(
- 'Firebase config w multiple sites missing "site" identifier',
- t => {
- const firebaseJson = fileURLToPath(new URL('./fixtures/failures/sites_missing_rewrites.json', import.meta.url));
- const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson};
- const error = t.throws(() => parseFirebaseConfiguration(config));
- t.is(error.message, 'See above output. See Hint code SAF1011 in README');
- }
-);
-
-test(
- 'Firebase config w multiple sites require a "hostingSite" to be specified',
- t => {
- const firebaseJson = fileURLToPath(new URL('./fixtures/failures/cf_multi_site_requires_hostingSite.json', import.meta.url));
- const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson};
- const error = t.throws(() => parseFirebaseConfiguration(config));
- t.is(error.message, 'See above output. See Hint code SAF1012 in README');
- }
-);
-
-test(
- 'Firebase config w multiple sites but no match found for a "hostingSite" specified in svelte.config.js adapter config',
- t => {
- const firebaseJson = fileURLToPath(new URL('./fixtures/failures/cf_multi_site_requires_hostingSite.json', import.meta.url));
- const config = {hostingSite: 'no_matching_site', sourceRewriteMatch: '**', firebaseJson};
- const error = t.throws(() => parseFirebaseConfiguration(config));
- t.is(error.message, 'See above output. See Hint code SAF1013 in README');
- }
-);
-
-test(
- 'Firebase config w missing "public"',
- t => {
- const firebaseJson = fileURLToPath(new URL('./fixtures/failures/site_missing_public.json', import.meta.url));
- const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson};
- const error = t.throws(() => parseFirebaseConfiguration(config));
- t.is(error.message, 'See above output. See Hint code SAF1050 in README');
- }
-);
-
-test('Firebase config w empty "public" string', t => {
- const firebaseJson = fileURLToPath(new URL('./fixtures/failures/site_empty_public.json', import.meta.url));
- const config = {sourceRewriteMatch: '**', firebaseJson};
- const error = t.throws(() => parseFirebaseConfiguration(config));
- t.is(error.message, 'See above output. See Hint code SAF1052 in README');
-});
-
-test(
- 'Firebase config w site missing "rewrites"',
- t => {
- const firebaseJson = fileURLToPath(new URL('./fixtures/failures/site_missing_rewrite.json', import.meta.url));
- const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson};
- const error = t.throws(() => parseFirebaseConfiguration(config));
- t.is(error.message, 'See above output. See Hint code SAF1020 in README');
- }
-);
-
-test(
- 'Firebase config w "rewrites" mismatch',
- t => {
- const firebaseJson = fileURLToPath(new URL('./fixtures/failures/cf_site_rewrite_mismatch.json', import.meta.url));
- const config = {hostingSite: undefined, sourceRewriteMatch: 'no_match', firebaseJson};
- const error = t.throws(() => parseFirebaseConfiguration(config));
- t.is(error.message, 'See above output. See Hint code SAF1021 in README');
- }
-);
-
-test(
- 'Firebase config w Cloud Run missing required "serviceId" field',
- t => {
- const firebaseJson = fileURLToPath(new URL('./fixtures/failures/cr_missing_serviceId.json', import.meta.url));
- const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson};
- const error = t.throws(() => parseFirebaseConfiguration(config));
- t.is(error.message, 'See above output. See Hint code SAF1030 in README');
- }
-);
-
-test(
- 'Firebase config w Cloud Run incompatible serviceId field',
- t => {
- const firebaseJson = fileURLToPath(new URL('./fixtures/failures/cr_invalid_serviceId.json', import.meta.url));
- const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson};
- const error = t.throws(() => parseFirebaseConfiguration(config));
- t.is(error.message, 'See above output. See Hint code SAF1031 in README');
- }
-);
-
-test(
- 'Firebase config w Cloud Run incompatible region field',
- t => {
- const firebaseJson = fileURLToPath(new URL('./fixtures/failures/cr_invalid_region.json', import.meta.url));
- const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson};
- const error = t.throws(() => parseFirebaseConfiguration(config));
- t.is(error.message, 'See above output. See Hint code SAF1032 in README');
- }
-);
-
-test(
- 'Firebase config w Cloud Function invalid name',
- t => {
- const firebaseJson = fileURLToPath(new URL('./fixtures/failures/cf_invalid_function_name.json', import.meta.url));
- const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson};
- const error = t.throws(() => parseFirebaseConfiguration(config));
- t.is(error.message, 'See above output. See Hint code SAF1040 in README');
- }
-);
-
-test(
- 'Firebase config w Cloud Functions & single site missing top-level functions',
- t => {
- const firebaseJson = fileURLToPath(new URL('./fixtures/failures/cf_site_missing_functions.json', import.meta.url));
- const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson};
- const error = t.throws(() => parseFirebaseConfiguration(config));
- t.is(error.message, 'See above output. See Hint code SAF1060 in README');
- }
-);
-
-// ValidCloudRunServiceId
-test('Cloud Run serviceId with all valid char types', t => {
- const result = validCloudRunServiceId('is-valid1');
- t.is(result, true);
-});
-
-test('Cloud Run serviceId with invalid dash prefix', t => {
- const result = validCloudRunServiceId('-not-valid1');
- t.is(result, false);
-});
-
-test('Cloud Run serviceId with invalid dash suffix', t => {
- const result = validCloudRunServiceId('not-valid1-');
- t.is(result, false);
-});
-
-test('Cloud Run serviceId with invalid uppercase char', t => {
- const result = validCloudRunServiceId('notValid1');
- t.is(result, false);
-});
-
-test('Cloud Run serviceId with invalid non-dash ($) symbol', t => {
- const result = validCloudRunServiceId('not$valid1');
- t.is(result, false);
-});
-
-test('Cloud Run serviceId with invalid non-dash (_) symbol', t => {
- const result = validCloudRunServiceId('not_valid1');
- t.is(result, false);
-});
-
-test('Cloud Run serviceId with invalid length', t => {
- const result = validCloudRunServiceId('aCloudFunctionsFunctionNameThatIsSeventyFiveCharactersLongWhichIsMoreThan63');
- t.is(result, false);
-});
-
-// ValidCloudFunctionName
-test('Cloud Function name with valid chars', t => {
- const result = validCloudFunctionName('lowercase_UPPERCASE_0123456789');
- t.is(result, true);
-});
-
-test('Cloud Function name with invalid dash', t => {
- const result = validCloudFunctionName('is-invalid');
- t.is(result, false);
-});
-
-test('Cloud Function name with invalid length', t => {
- const result = validCloudFunctionName('aCloudFunctionsFunctionNameThatIsSeventyFiveCharactersLongWhichIsMoreThan63');
- t.is(result, false);
-});
-
-// EnsureStaticResourceDirsDiffer
-test('Static asset source and dest different dirs', t => {
- const error = t.notThrows(() => ensureStaticResourceDirsDiffer({source: 'a', dest: 'b'}));
- t.is(error, undefined);
-});
-
-test(
- 'Static asset source and dest the same dir',
- t => {
- const error = t.throws(() => ensureStaticResourceDirsDiffer({source: 'a', dest: 'a'}));
- t.is(error.message, 'See above output. See Hint code SAF1051 in README');
- }
-);
-
-// EnsureCompatibleCloudFunctionVersion
-test('Valid Function runtime version in package.json', t => {
- const error = t.notThrows(() => ensureCompatibleCloudFunctionVersion({functionsPackageJsonEngine: '14'}));
- t.is(error, undefined);
-});
-test('Valid Function runtime version in firebase.json', t => {
- const error = t.notThrows(() => ensureCompatibleCloudFunctionVersion({firebaseJsonFunctionsRuntime: 'nodejs14'}));
- t.is(error, undefined);
-});
-
-test(
- 'No Function runtime provided',
- t => {
- const error = t.throws(() => ensureCompatibleCloudFunctionVersion({}));
- t.is(error.message, 'See above output. See Hint code SAF1061 in README');
- }
-);
-test(
- 'Invalid Function runtime in package.json',
- t => {
- const error = t.throws(() => ensureCompatibleCloudFunctionVersion({functionsPackageJsonEngine: '12'}));
- t.is(error.message, 'See above output. See Hint code SAF1061 in README');
- }
-);
-test(
- 'Invalid Function runtime in firebase.json',
- t => {
- const error = t.throws(() => ensureCompatibleCloudFunctionVersion({firebaseJsonFunctionsRuntime: 'nodejs12'}));
- t.is(error.message, 'See above output. See Hint code SAF1061 in README');
- }
-);
diff --git a/tests/integration/functions_single_site/.firebaserc b/tests/integration/functions_single_site/.firebaserc
new file mode 100644
index 0000000..d8085fd
--- /dev/null
+++ b/tests/integration/functions_single_site/.firebaserc
@@ -0,0 +1 @@
+{"projects":{"default":"demo"}}
diff --git a/tests/integration/functions_single_site/firebase.json b/tests/integration/functions_single_site/firebase.json
new file mode 100644
index 0000000..839c82b
--- /dev/null
+++ b/tests/integration/functions_single_site/firebase.json
@@ -0,0 +1,26 @@
+{
+ "hosting": {
+ "public": "public",
+ "site": "svelte-func-single-site",
+ "rewrites": [
+ {
+ "source": "**",
+ "function": "sveltekit"
+ }
+ ]
+ },
+ "functions": {
+ "source": "functions"
+ },
+ "emulators": {
+ "ui": {
+ "enabled": false
+ },
+ "hosting": {
+ "port": 8685
+ },
+ "functions": {
+ "port": 5001
+ }
+ }
+}
diff --git a/examples/nested_app_dirs/functions/.gitignore b/tests/integration/functions_single_site/functions/.gitignore
similarity index 100%
rename from examples/nested_app_dirs/functions/.gitignore
rename to tests/integration/functions_single_site/functions/.gitignore
diff --git a/examples/nested_app_dirs/functions/index.js b/tests/integration/functions_single_site/functions/index.js
similarity index 100%
rename from examples/nested_app_dirs/functions/index.js
rename to tests/integration/functions_single_site/functions/index.js
diff --git a/examples/nested_app_dirs/functions/package.json b/tests/integration/functions_single_site/functions/package.json
similarity index 100%
rename from examples/nested_app_dirs/functions/package.json
rename to tests/integration/functions_single_site/functions/package.json
diff --git a/examples/run_single_site/svelte.config.js b/tests/integration/functions_single_site/svelte.config.js
similarity index 100%
rename from examples/run_single_site/svelte.config.js
rename to tests/integration/functions_single_site/svelte.config.js
diff --git a/tests/integration/integration-test.bash b/tests/integration/integration-test.bash
new file mode 100755
index 0000000..420702c
--- /dev/null
+++ b/tests/integration/integration-test.bash
@@ -0,0 +1,74 @@
+#!/usr/bin/env bash
+
+# undefined vars are errors
+set -u
+IFS=$'\n\t'
+
+# Execute integration test for provided dir. Inits SvelteKit Todo app template
+# and adds Firebase Adapter with configuration from provided dir.
+# Assert build assets for static and compute are in expected locations
+# Requires deps in .tool-versions at repo root
+#
+# Usage:
+#
+# tests/integration/integration-test.bash "functions_single_site" "public/about/index.html" "functions/sveltekit/index.js" "."
+# tests/integration/integration-test.bash "nested_app_dirs" "public/about/index.html" "functions/sveltekit/index.js" "app"
+# tests/integration/integration-test.bash "run_custom_build_dir" "public/about/index.html" "custom-cloud-run-build-dir/index.js" "."
+# tests/integration/integration-test.bash "run_single_site" "public/about/index.html" ".cloudrun/index.js" "."
+
+SOURCE_DIR="$1"
+PUBLIC_FILENAME="$2"
+KIT_FILENAME="$3"
+NESTED_APP_DIR="${4}"
+
+INDICATOR="====> "
+SCRIPT_PATH=$(dirname "$(realpath -s "$0")")
+TEST_DIR="$(mktemp -dt "svelte-adapter-firebase-test-${SOURCE_DIR}-XXXX")"
+
+# Cleanup files on exit
+trap 'echo "${INDICATOR}Exiting, removing ${TEST_DIR}" && rm -rf -- "$TEST_DIR"' EXIT
+
+echo "${INDICATOR}TEST_DIR: ${TEST_DIR}"
+echo "${INDICATOR}PWD: ${PWD}"
+
+echo "${INDICATOR}Install svelte-adapter-firebase ${SCRIPT_PATH}/../../ deps"
+npm install
+
+echo "${INDICATOR}init SvelteKit Todos app"
+yes "" | "$(npm init svelte@next "${TEST_DIR}/${NESTED_APP_DIR}")"
+echo "${INDICATOR}Complete SvelteKit init"
+
+cp -R "${SCRIPT_PATH}"/"${SOURCE_DIR}"/* "${TEST_DIR}"
+cp "${SCRIPT_PATH}/${SOURCE_DIR}/.firebaserc" "${TEST_DIR}/.firebaserc"
+cp ".tool-versions" "${TEST_DIR}/.tool-versions"
+
+cd "${TEST_DIR}/${NESTED_APP_DIR}" || exit 1
+echo "${INDICATOR}PWD after cd to TEST_DIR: ${PWD}"
+
+echo "${INDICATOR}Set package.json:scripts.build to verbose mode"
+sed -i -e 's/svelte-kit build/svelte-kit build --verbose/g' "${TEST_DIR}/${NESTED_APP_DIR}/package.json"
+
+echo "${INDICATOR}Install kit template deps"
+npm install
+
+echo "${INDICATOR}Install svelte-adapter-firebase from ${SCRIPT_PATH}/../../"
+npm install "${SCRIPT_PATH}/../../"
+
+echo "${INDICATOR}Build Kit todos site"
+npm run build
+
+# Check ${PUBLIC_FILENAME} exists
+if [ ! -f "${TEST_DIR}/${NESTED_APP_DIR}/${PUBLIC_FILENAME}" ]; then
+ echo "${INDICATOR}FAILED to find ${TEST_DIR}/${NESTED_APP_DIR}/${PUBLIC_FILENAME}"
+ exit 1
+fi
+
+# Check ${KIT_FILENAME}
+if [ ! -f "${TEST_DIR}/${KIT_FILENAME}" ]; then
+ echo "${INDICATOR}FAILED to find ${TEST_DIR}/${KIT_FILENAME}"
+ exit 1
+fi
+
+echo "${INDICATOR}Success"
+
+
diff --git a/tests/integration/nested_app_dirs/.firebaserc b/tests/integration/nested_app_dirs/.firebaserc
new file mode 100644
index 0000000..d8085fd
--- /dev/null
+++ b/tests/integration/nested_app_dirs/.firebaserc
@@ -0,0 +1 @@
+{"projects":{"default":"demo"}}
diff --git a/examples/nested_app_dirs/app/svelte.config.js b/tests/integration/nested_app_dirs/app/svelte.config.js
similarity index 100%
rename from examples/nested_app_dirs/app/svelte.config.js
rename to tests/integration/nested_app_dirs/app/svelte.config.js
diff --git a/examples/nested_app_dirs/firebase.json b/tests/integration/nested_app_dirs/firebase.json
similarity index 69%
rename from examples/nested_app_dirs/firebase.json
rename to tests/integration/nested_app_dirs/firebase.json
index 86a3e89..a97e698 100644
--- a/examples/nested_app_dirs/firebase.json
+++ b/tests/integration/nested_app_dirs/firebase.json
@@ -14,19 +14,13 @@
},
"emulators": {
"ui": {
- "enabled": true
+ "enabled": false
+ },
+ "hosting": {
+ "port": 8685
},
"functions": {
"port": 5001
- },
- "auth": {
- "port": 9099
- },
- "firestore": {
- "port": 8080
- },
- "pubsub": {
- "port": 8085
}
}
}
diff --git a/tests/integration/nested_app_dirs/functions/.gitignore b/tests/integration/nested_app_dirs/functions/.gitignore
new file mode 100644
index 0000000..6b6c9af
--- /dev/null
+++ b/tests/integration/nested_app_dirs/functions/.gitignore
@@ -0,0 +1,5 @@
+node_modules/
+
+# Specific to this app's firebase.json
+# ignore SvelteKit build output in Cloud Function
+sveltekit/
diff --git a/tests/integration/nested_app_dirs/functions/index.js b/tests/integration/nested_app_dirs/functions/index.js
new file mode 100644
index 0000000..aa78ee6
--- /dev/null
+++ b/tests/integration/nested_app_dirs/functions/index.js
@@ -0,0 +1,13 @@
+const functions = require('firebase-functions');
+
+let sveltekitServer;
+exports.sveltekit = functions.https.onRequest(async (request, response) => {
+ if (!sveltekitServer) {
+ functions.logger.info('Initialising SvelteKit SSR Handler');
+ sveltekitServer = require('./sveltekit/index').default;
+ functions.logger.info('SvelteKit SSR Handler initialised!');
+ }
+
+ functions.logger.info('Requested resource: ' + request.originalUrl);
+ return sveltekitServer(request, response);
+});
diff --git a/tests/integration/nested_app_dirs/functions/package.json b/tests/integration/nested_app_dirs/functions/package.json
new file mode 100644
index 0000000..48bbe8c
--- /dev/null
+++ b/tests/integration/nested_app_dirs/functions/package.json
@@ -0,0 +1,23 @@
+{
+ "name": "functions",
+ "description": "Cloud Functions for Firebase",
+ "scripts": {
+ "serve": "firebase emulators:start --only functions",
+ "shell": "firebase functions:shell",
+ "start": "npm run shell",
+ "deploy": "firebase deploy --only functions",
+ "logs": "firebase functions:log"
+ },
+ "engines": {
+ "node": "14"
+ },
+ "main": "index.js",
+ "dependencies": {
+ "firebase-admin": "^9.2.0",
+ "firebase-functions": "^3.11.0"
+ },
+ "devDependencies": {
+ "firebase-functions-test": "^0.2.0"
+ },
+ "private": true
+}
diff --git a/tests/integration/run_custom_build_dir/.firebaserc b/tests/integration/run_custom_build_dir/.firebaserc
new file mode 100644
index 0000000..d8085fd
--- /dev/null
+++ b/tests/integration/run_custom_build_dir/.firebaserc
@@ -0,0 +1 @@
+{"projects":{"default":"demo"}}
diff --git a/examples/run_custom_build_dir/firebase.json b/tests/integration/run_custom_build_dir/firebase.json
similarity index 69%
rename from examples/run_custom_build_dir/firebase.json
rename to tests/integration/run_custom_build_dir/firebase.json
index cb37067..e9ac037 100644
--- a/examples/run_custom_build_dir/firebase.json
+++ b/tests/integration/run_custom_build_dir/firebase.json
@@ -13,19 +13,13 @@
},
"emulators": {
"ui": {
- "enabled": true
+ "enabled": false
+ },
+ "hosting": {
+ "port": 8685
},
"functions": {
"port": 5001
- },
- "auth": {
- "port": 9099
- },
- "firestore": {
- "port": 8080
- },
- "pubsub": {
- "port": 8085
}
}
}
diff --git a/examples/run_custom_build_dir/svelte.config.js b/tests/integration/run_custom_build_dir/svelte.config.js
similarity index 100%
rename from examples/run_custom_build_dir/svelte.config.js
rename to tests/integration/run_custom_build_dir/svelte.config.js
diff --git a/tests/integration/run_single_site/.firebaserc b/tests/integration/run_single_site/.firebaserc
new file mode 100644
index 0000000..d8085fd
--- /dev/null
+++ b/tests/integration/run_single_site/.firebaserc
@@ -0,0 +1 @@
+{"projects":{"default":"demo"}}
diff --git a/examples/run_single_site/firebase.json b/tests/integration/run_single_site/firebase.json
similarity index 68%
rename from examples/run_single_site/firebase.json
rename to tests/integration/run_single_site/firebase.json
index 21103e3..555991c 100644
--- a/examples/run_single_site/firebase.json
+++ b/tests/integration/run_single_site/firebase.json
@@ -13,19 +13,13 @@
},
"emulators": {
"ui": {
- "enabled": true
+ "enabled": false
+ },
+ "hosting": {
+ "port": 8685
},
"functions": {
"port": 5001
- },
- "auth": {
- "port": 9099
- },
- "firestore": {
- "port": 8080
- },
- "pubsub": {
- "port": 8085
}
}
}
diff --git a/tests/integration/run_single_site/svelte.config.js b/tests/integration/run_single_site/svelte.config.js
new file mode 100644
index 0000000..bb640d7
--- /dev/null
+++ b/tests/integration/run_single_site/svelte.config.js
@@ -0,0 +1,12 @@
+import firebase from 'svelte-adapter-firebase';
+
+/** @type {import('@sveltejs/kit').Config} */
+const config = {
+ kit: {
+ // Hydrate the element in src/app.html
+ adapter: firebase(),
+ target: '#svelte'
+ }
+};
+
+export default config;
diff --git a/tests/fixtures/failures/cf_invalid_function_name.json b/tests/unit/fixtures/failures/cf_invalid_function_name.json
similarity index 100%
rename from tests/fixtures/failures/cf_invalid_function_name.json
rename to tests/unit/fixtures/failures/cf_invalid_function_name.json
diff --git a/tests/fixtures/failures/cf_multi_site_no_hostingSite_match.json b/tests/unit/fixtures/failures/cf_multi_site_no_hostingSite_match.json
similarity index 100%
rename from tests/fixtures/failures/cf_multi_site_no_hostingSite_match.json
rename to tests/unit/fixtures/failures/cf_multi_site_no_hostingSite_match.json
diff --git a/tests/fixtures/failures/cf_multi_site_requires_hostingSite.json b/tests/unit/fixtures/failures/cf_multi_site_requires_hostingSite.json
similarity index 100%
rename from tests/fixtures/failures/cf_multi_site_requires_hostingSite.json
rename to tests/unit/fixtures/failures/cf_multi_site_requires_hostingSite.json
diff --git a/tests/fixtures/failures/cf_site_missing_functions.json b/tests/unit/fixtures/failures/cf_site_missing_functions.json
similarity index 100%
rename from tests/fixtures/failures/cf_site_missing_functions.json
rename to tests/unit/fixtures/failures/cf_site_missing_functions.json
diff --git a/tests/fixtures/failures/cf_site_rewrite_mismatch.json b/tests/unit/fixtures/failures/cf_site_rewrite_mismatch.json
similarity index 100%
rename from tests/fixtures/failures/cf_site_rewrite_mismatch.json
rename to tests/unit/fixtures/failures/cf_site_rewrite_mismatch.json
diff --git a/tests/fixtures/failures/cr_invalid_region.json b/tests/unit/fixtures/failures/cr_invalid_region.json
similarity index 100%
rename from tests/fixtures/failures/cr_invalid_region.json
rename to tests/unit/fixtures/failures/cr_invalid_region.json
diff --git a/tests/fixtures/failures/cr_invalid_serviceId.json b/tests/unit/fixtures/failures/cr_invalid_serviceId.json
similarity index 100%
rename from tests/fixtures/failures/cr_invalid_serviceId.json
rename to tests/unit/fixtures/failures/cr_invalid_serviceId.json
diff --git a/tests/fixtures/failures/cr_missing_serviceId.json b/tests/unit/fixtures/failures/cr_missing_serviceId.json
similarity index 100%
rename from tests/fixtures/failures/cr_missing_serviceId.json
rename to tests/unit/fixtures/failures/cr_missing_serviceId.json
diff --git a/tests/fixtures/failures/invalid.json b/tests/unit/fixtures/failures/invalid.json
similarity index 100%
rename from tests/fixtures/failures/invalid.json
rename to tests/unit/fixtures/failures/invalid.json
diff --git a/tests/fixtures/failures/missing_hosting.json b/tests/unit/fixtures/failures/missing_hosting.json
similarity index 100%
rename from tests/fixtures/failures/missing_hosting.json
rename to tests/unit/fixtures/failures/missing_hosting.json
diff --git a/tests/fixtures/failures/site_empty_public.json b/tests/unit/fixtures/failures/site_empty_public.json
similarity index 100%
rename from tests/fixtures/failures/site_empty_public.json
rename to tests/unit/fixtures/failures/site_empty_public.json
diff --git a/tests/fixtures/failures/site_missing_public.json b/tests/unit/fixtures/failures/site_missing_public.json
similarity index 100%
rename from tests/fixtures/failures/site_missing_public.json
rename to tests/unit/fixtures/failures/site_missing_public.json
diff --git a/tests/fixtures/failures/site_missing_rewrite.json b/tests/unit/fixtures/failures/site_missing_rewrite.json
similarity index 100%
rename from tests/fixtures/failures/site_missing_rewrite.json
rename to tests/unit/fixtures/failures/site_missing_rewrite.json
diff --git a/tests/fixtures/failures/sites_missing_rewrites.json b/tests/unit/fixtures/failures/sites_missing_rewrites.json
similarity index 100%
rename from tests/fixtures/failures/sites_missing_rewrites.json
rename to tests/unit/fixtures/failures/sites_missing_rewrites.json
diff --git a/tests/fixtures/successes/cf_site.json b/tests/unit/fixtures/successes/cf_site.json
similarity index 100%
rename from tests/fixtures/successes/cf_site.json
rename to tests/unit/fixtures/successes/cf_site.json
diff --git a/tests/fixtures/successes/cf_sites.json b/tests/unit/fixtures/successes/cf_sites.json
similarity index 100%
rename from tests/fixtures/successes/cf_sites.json
rename to tests/unit/fixtures/successes/cf_sites.json
diff --git a/tests/fixtures/successes/cr_site.json b/tests/unit/fixtures/successes/cr_site.json
similarity index 100%
rename from tests/fixtures/successes/cr_site.json
rename to tests/unit/fixtures/successes/cr_site.json
diff --git a/tests/fixtures/successes/cr_sites.json b/tests/unit/fixtures/successes/cr_sites.json
similarity index 100%
rename from tests/fixtures/successes/cr_sites.json
rename to tests/unit/fixtures/successes/cr_sites.json
diff --git a/tests/unit/src/files/firebase-to-svelte-kit.test.js b/tests/unit/src/files/firebase-to-svelte-kit.test.js
new file mode 100644
index 0000000..c42075c
--- /dev/null
+++ b/tests/unit/src/files/firebase-to-svelte-kit.test.js
@@ -0,0 +1,112 @@
+import {test} from 'uvu';
+import * as assert from 'uvu/assert'; // eslint-disable-line node/file-extension-in-import
+
+import {toSvelteKitRequest, toSvelteKitHeaders} from '../../../../src/files/firebase-to-svelte-kit.js';
+
+// Headers
+test('leave headers without string[] untouched', () => {
+ const input = {
+ accept: 'something',
+ 'accept-language': 'en'
+ };
+ const expected = input;
+ const result = toSvelteKitHeaders(input);
+
+ assert.equal(result, expected, 'match');
+});
+
+test('convert string[] headers to csv string values', () => {
+ const expected = {
+ accept: 'something',
+ 'accept-language': 'en',
+ 'set-cookie': 'some,cookie,data'
+ };
+ const result = toSvelteKitHeaders({
+ accept: 'something',
+ 'accept-language': 'en',
+ 'set-cookie': ['some', 'cookie', 'data']
+ });
+
+ assert.equal(result, expected, 'string[] has been converted to csv string');
+});
+
+test('convert string[] headers of any kind to csv string values', () => {
+ const expected = {
+ accept: 'something',
+ 'accept-language': 'en',
+ 'user-defined-header': 'some,user,defined,header,data'
+ };
+ const result = toSvelteKitHeaders({
+ accept: 'something',
+ 'accept-language': 'en',
+ 'user-defined-header': ['some', 'user', 'defined', 'header', 'data']
+ });
+
+ assert.equal(result, expected, 'string[] has been converted to csv string');
+});
+
+// Request
+test('firebase-functions.https.request GET is converted to SvelteKit Incoming request type correctly', () => {
+ const firebaseRequest = {
+ method: 'GET',
+ headers: {
+ 'accept-language': 'en',
+ 'set-cookie': ['some', 'cookie', 'data'],
+ host: 'us-central1-func.cloudfunctions.net',
+ 'x-forwarded-proto': 'https'
+ },
+ url: '/url?some=thing'
+ };
+
+ const expectedKitRequest = {
+ method: 'GET',
+ headers: {
+ 'accept-language': 'en',
+ 'set-cookie': 'some,cookie,data',
+ host: 'us-central1-func.cloudfunctions.net',
+ 'x-forwarded-proto': 'https'
+ },
+ rawBody: new Uint8Array(),
+ host: 'https://us-central1-func.cloudfunctions.net',
+ path: '/url',
+ query: new URL('/url?some=thing', 'https://us-central1-func.cloudfunctions.net').searchParams
+ };
+
+ const result = toSvelteKitRequest(firebaseRequest);
+
+ assert.equal(result, expectedKitRequest, 'match');
+});
+
+test('firebase-functions.https.request POST is converted to SvelteKit Incoming request type correctly', () => {
+ const firebaseRequest = {
+ method: 'POST',
+ headers: {
+ 'accept-language': 'en',
+ 'set-cookie': ['some', 'cookie', 'data'],
+ host: 'us-central1-func.cloudfunctions.net',
+ 'x-forwarded-proto': 'https'
+ },
+ rawBody: Buffer.from('some-data', 'utf8'),
+ url: '/url?some=thing'
+ };
+
+ const expectedKitRequest = {
+ method: 'POST',
+ headers: {
+ 'accept-language': 'en',
+ 'set-cookie': 'some,cookie,data',
+ host: 'us-central1-func.cloudfunctions.net',
+ 'x-forwarded-proto': 'https'
+ },
+ rawBody: Buffer.from('some-data', 'utf-8'),
+ host: 'https://us-central1-func.cloudfunctions.net',
+ path: '/url',
+ query: new URL('/url?some=thing', 'https://us-central1-func.cloudfunctions.net').searchParams
+ };
+
+ const result = toSvelteKitRequest(firebaseRequest);
+
+ assert.equal(result, expectedKitRequest, 'match');
+});
+
+test.run();
diff --git a/tests/unit/src/utils.test.js b/tests/unit/src/utils.test.js
new file mode 100644
index 0000000..c07348d
--- /dev/null
+++ b/tests/unit/src/utils.test.js
@@ -0,0 +1,276 @@
+import {fileURLToPath} from 'url';
+import path from 'path';
+import {test} from 'uvu';
+import * as assert from 'uvu/assert'; // eslint-disable-line node/file-extension-in-import
+import {ensureCompatibleCloudFunctionVersion, ensureStaticResourceDirsDiffer, parseFirebaseConfiguration, validCloudFunctionName, validCloudRunServiceId} from '../../../src/utils.js';
+
+// ParseFirebaseConfiguration: Valid configs
+test(
+ 'Firebase config w Cloud Functions & single site',
+ () => {
+ const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson: fileURLToPath(new URL('../fixtures/successes/cf_site.json', import.meta.url))};
+ const result = parseFirebaseConfiguration(config);
+ const expectedResult = {functions: {name: 'some_func', source: path.join(path.dirname(config.firebaseJson), 'functions'), runtime: undefined}, cloudRun: false, publicDir: path.join(path.dirname(config.firebaseJson), 'app'), firebaseJsonDir: path.dirname(config.firebaseJson)};
+
+ assert.equal(result, expectedResult);
+ }
+);
+
+test(
+ 'Firebase config w Cloud Functions & multiple sites',
+ () => {
+ const config = {hostingSite: 'app', sourceRewriteMatch: '**', firebaseJson: fileURLToPath(new URL('../fixtures/successes/cf_sites.json', import.meta.url))};
+ const result = parseFirebaseConfiguration(config);
+ const expectedResult = {functions: {name: 'some_func', source: path.join(path.dirname(config.firebaseJson), 'functions'), runtime: undefined}, cloudRun: false, publicDir: path.join(path.dirname(config.firebaseJson), 'app'), firebaseJsonDir: path.dirname(config.firebaseJson)};
+
+ assert.equal(result, expectedResult);
+ }
+);
+
+test(
+ 'Firebase config w Cloud Run & single site',
+ () => {
+ const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson: fileURLToPath(new URL('../fixtures/successes/cr_site.json', import.meta.url))};
+ const result = parseFirebaseConfiguration(config);
+ const expectedResult = {functions: false, cloudRun: {serviceId: 'some-service', region: 'us-central1'}, publicDir: path.join(path.dirname(config.firebaseJson), 'app'), firebaseJsonDir: path.dirname(config.firebaseJson)};
+
+ assert.equal(result, expectedResult);
+ }
+);
+
+test(
+ 'Firebase config w Cloud Run & multiple sites',
+ () => {
+ const config = {hostingSite: 'app', sourceRewriteMatch: '**', firebaseJson: fileURLToPath(new URL('../fixtures/successes/cr_sites.json', import.meta.url))};
+ const result = parseFirebaseConfiguration(config);
+ const expectedResult = {functions: false, cloudRun: {serviceId: 'some-service', region: 'us-central1'}, publicDir: path.join(path.dirname(config.firebaseJson), 'app'), firebaseJsonDir: path.dirname(config.firebaseJson)};
+
+ assert.equal(result, expectedResult);
+ }
+);
+
+// ParseFirebaseConfiguration: Invalid configs
+test(
+ 'Firebase config does not exist',
+ () => {
+ const firebaseJson = fileURLToPath(new URL('./does_not_exist.json', import.meta.url));
+ const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson};
+ assert.throws(() => parseFirebaseConfiguration(config), 'See above output. See Hint code SAF1000 in README');
+ }
+);
+
+test(
+ 'Firebase config is invalid json',
+ () => {
+ const firebaseJson = fileURLToPath(new URL('../fixtures/failures/invalid.json', import.meta.url));
+ const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson};
+ assert.throws(() => parseFirebaseConfiguration(config), 'See above output. See Hint code SAF1001 in README');
+ }
+);
+
+test(
+ 'Firebase config without "hosting" field',
+ () => {
+ const firebaseJson = fileURLToPath(new URL('../fixtures/failures/missing_hosting.json', import.meta.url));
+ const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson};
+ assert.throws(() => parseFirebaseConfiguration(config), 'See above output. See Hint code SAF1010 in README');
+ }
+);
+
+test(
+ 'Firebase config w multiple sites missing "site" identifier',
+ () => {
+ const firebaseJson = fileURLToPath(new URL('../fixtures/failures/sites_missing_rewrites.json', import.meta.url));
+ const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson};
+ assert.throws(() => parseFirebaseConfiguration(config), 'See above output. See Hint code SAF1011 in README');
+ }
+);
+
+test(
+ 'Firebase config w multiple sites require a "hostingSite" to be specified',
+ () => {
+ const firebaseJson = fileURLToPath(new URL('../fixtures/failures/cf_multi_site_requires_hostingSite.json', import.meta.url));
+ const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson};
+ assert.throws(() => parseFirebaseConfiguration(config), 'See above output. See Hint code SAF1012 in README');
+ }
+);
+
+test(
+ 'Firebase config w multiple sites but no match found for a "hostingSite" specified in svelte.config.js adapter config',
+ () => {
+ const firebaseJson = fileURLToPath(new URL('../fixtures/failures/cf_multi_site_requires_hostingSite.json', import.meta.url));
+ const config = {hostingSite: 'no_matching_site', sourceRewriteMatch: '**', firebaseJson};
+ assert.throws(() => parseFirebaseConfiguration(config), 'See above output. See Hint code SAF1013 in README');
+ }
+);
+
+test(
+ 'Firebase config w missing "public"',
+ () => {
+ const firebaseJson = fileURLToPath(new URL('../fixtures/failures/site_missing_public.json', import.meta.url));
+ const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson};
+ assert.throws(() => parseFirebaseConfiguration(config), 'See above output. See Hint code SAF1050 in README');
+ }
+);
+
+test('Firebase config w empty "public" string', () => {
+ const firebaseJson = fileURLToPath(new URL('../fixtures/failures/site_empty_public.json', import.meta.url));
+ const config = {sourceRewriteMatch: '**', firebaseJson};
+ assert.throws(() => parseFirebaseConfiguration(config), 'See above output. See Hint code SAF1052 in README');
+});
+
+test(
+ 'Firebase config w site missing "rewrites"',
+ () => {
+ const firebaseJson = fileURLToPath(new URL('../fixtures/failures/site_missing_rewrite.json', import.meta.url));
+ const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson};
+ assert.throws(() => parseFirebaseConfiguration(config), 'See above output. See Hint code SAF1020 in README');
+ }
+);
+
+test(
+ 'Firebase config w "rewrites" mismatch',
+ () => {
+ const firebaseJson = fileURLToPath(new URL('../fixtures/failures/cf_site_rewrite_mismatch.json', import.meta.url));
+ const config = {hostingSite: undefined, sourceRewriteMatch: 'no_match', firebaseJson};
+ assert.throws(() => parseFirebaseConfiguration(config), 'See above output. See Hint code SAF1021 in README');
+ }
+);
+
+test(
+ 'Firebase config w Cloud Run missing required "serviceId" field',
+ () => {
+ const firebaseJson = fileURLToPath(new URL('../fixtures/failures/cr_missing_serviceId.json', import.meta.url));
+ const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson};
+ assert.throws(() => parseFirebaseConfiguration(config), 'See above output. See Hint code SAF1030 in README');
+ }
+);
+
+test(
+ 'Firebase config w Cloud Run incompatible serviceId field',
+ () => {
+ const firebaseJson = fileURLToPath(new URL('../fixtures/failures/cr_invalid_serviceId.json', import.meta.url));
+ const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson};
+ assert.throws(() => parseFirebaseConfiguration(config), 'See above output. See Hint code SAF1031 in README');
+ }
+);
+
+test(
+ 'Firebase config w Cloud Run incompatible region field',
+ () => {
+ const firebaseJson = fileURLToPath(new URL('../fixtures/failures/cr_invalid_region.json', import.meta.url));
+ const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson};
+ assert.throws(() => parseFirebaseConfiguration(config), 'See above output. See Hint code SAF1032 in README');
+ }
+);
+
+test(
+ 'Firebase config w Cloud Function invalid name',
+ () => {
+ const firebaseJson = fileURLToPath(new URL('../fixtures/failures/cf_invalid_function_name.json', import.meta.url));
+ const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson};
+ assert.throws(() => parseFirebaseConfiguration(config), 'See above output. See Hint code SAF1040 in README');
+ }
+);
+
+test(
+ 'Firebase config w Cloud Functions & single site missing top-level functions',
+ () => {
+ const firebaseJson = fileURLToPath(new URL('../fixtures/failures/cf_site_missing_functions.json', import.meta.url));
+ const config = {hostingSite: undefined, sourceRewriteMatch: '**', firebaseJson};
+ assert.throws(() => parseFirebaseConfiguration(config), 'See above output. See Hint code SAF1060 in README');
+ }
+);
+
+// ValidCloudRunServiceId
+test('Cloud Run serviceId with all valid char types', () => {
+ const result = validCloudRunServiceId('is-valid1');
+ assert.is(result, true);
+});
+
+test('Cloud Run serviceId with invalid dash prefix', () => {
+ const result = validCloudRunServiceId('-not-valid1');
+ assert.is(result, false);
+});
+
+test('Cloud Run serviceId with invalid dash suffix', () => {
+ const result = validCloudRunServiceId('not-valid1-');
+ assert.is(result, false);
+});
+
+test('Cloud Run serviceId with invalid uppercase char', () => {
+ const result = validCloudRunServiceId('notValid1');
+ assert.is(result, false);
+});
+
+test('Cloud Run serviceId with invalid non-dash ($) symbol', () => {
+ const result = validCloudRunServiceId('not$valid1');
+ assert.is(result, false);
+});
+
+test('Cloud Run serviceId with invalid non-dash (_) symbol', () => {
+ const result = validCloudRunServiceId('not_valid1');
+ assert.is(result, false);
+});
+
+test('Cloud Run serviceId with invalid length', () => {
+ const result = validCloudRunServiceId('aCloudFunctionsFunctionNameThatIsSeventyFiveCharactersLongWhichIsMoreThan63');
+ assert.is(result, false);
+});
+
+// ValidCloudFunctionName
+test('Cloud Function name with valid chars', () => {
+ const result = validCloudFunctionName('lowercase_UPPERCASE_0123456789');
+ assert.is(result, true);
+});
+
+test('Cloud Function name with invalid dash', () => {
+ const result = validCloudFunctionName('is-invalid');
+ assert.is(result, false);
+});
+
+test('Cloud Function name with invalid length', () => {
+ const result = validCloudFunctionName('aCloudFunctionsFunctionNameThatIsSeventyFiveCharactersLongWhichIsMoreThan63');
+ assert.is(result, false);
+});
+
+// EnsureStaticResourceDirsDiffer
+test('Static asset source and dest different dirs', () => {
+ assert.not.throws(() => ensureStaticResourceDirsDiffer({source: 'a', dest: 'b'}));
+});
+
+test(
+ 'Static asset source and dest the same dir',
+ () => {
+ assert.throws(() => ensureStaticResourceDirsDiffer({source: 'a', dest: 'a'}), 'See above output. See Hint code SAF1051 in README');
+ }
+);
+
+// EnsureCompatibleCloudFunctionVersion
+test('Valid Function runtime version in package.json', () => {
+ assert.not.throws(() => ensureCompatibleCloudFunctionVersion({functionsPackageJsonEngine: '14'}));
+});
+test('Valid Function runtime version in firebase.json', () => {
+ assert.not.throws(() => ensureCompatibleCloudFunctionVersion({firebaseJsonFunctionsRuntime: 'nodejs14'}));
+});
+
+test(
+ 'No Function runtime provided',
+ () => {
+ assert.throws(() => ensureCompatibleCloudFunctionVersion({}), 'See above output. See Hint code SAF1061 in README');
+ }
+);
+test(
+ 'Invalid Function runtime in package.json',
+ () => {
+ assert.throws(() => ensureCompatibleCloudFunctionVersion({functionsPackageJsonEngine: '12'}), 'See above output. See Hint code SAF1061 in README');
+ }
+);
+test(
+ 'Invalid Function runtime in firebase.json',
+ () => {
+ assert.throws(() => ensureCompatibleCloudFunctionVersion({firebaseJsonFunctionsRuntime: 'nodejs12'}), 'See above output. See Hint code SAF1061 in README');
+ }
+);
+
+test.run();