From d49ebec908a375bbf7502b87172d2ef7e3d09092 Mon Sep 17 00:00:00 2001 From: Simon M Date: Wed, 6 Sep 2023 09:01:21 +0100 Subject: [PATCH 1/8] Add sync --migration --- e2e/nx-firebase-e2e/test-utils/index.ts | 156 +++++++++--------- e2e/nx-firebase-e2e/tests/nx-firebase.spec.ts | 34 ++++ .../src/generators/sync/lib/index.ts | 1 + .../src/generators/sync/lib/migrate.ts | 90 ++++++++++ .../src/generators/sync/schema.d.ts | 1 + .../src/generators/sync/schema.json | 7 +- .../nx-firebase/src/generators/sync/sync.ts | 29 ++-- .../nx-firebase/src/utils/firebase-config.ts | 1 + 8 files changed, 232 insertions(+), 87 deletions(-) create mode 100644 packages/nx-firebase/src/generators/sync/lib/migrate.ts diff --git a/e2e/nx-firebase-e2e/test-utils/index.ts b/e2e/nx-firebase-e2e/test-utils/index.ts index 779eba8e..b28c4ab8 100644 --- a/e2e/nx-firebase-e2e/test-utils/index.ts +++ b/e2e/nx-firebase-e2e/test-utils/index.ts @@ -205,87 +205,93 @@ export function addImport(mainTs: string, addition: string) { return replaced } + +export function expectedAppProjectTargets(projectDir: string, projectName: string) { + return { + build: { + executor: 'nx:run-commands', + options: { + command: `echo Build succeeded.`, + }, + }, + watch: { + executor: 'nx:run-commands', + options: { + command: `nx run-many --targets=build --projects=tag:firebase:dep:${projectName} --parallel=100 --watch`, + }, + }, + lint: { + executor: 'nx:run-commands', + options: { + command: `nx run-many --targets=lint --projects=tag:firebase:dep:${projectName} --parallel=100`, + }, + }, + test: { + executor: 'nx:run-commands', + options: { + command: `nx run-many --targets=test --projects=tag:firebase:dep:${projectName} --parallel=100`, + }, + }, + firebase: { + executor: 'nx:run-commands', + options: { + command: `firebase --config=firebase.json`, + }, + configurations: { + production: { + command: `firebase --config=firebase.json`, + }, + }, + }, + killports: { + executor: 'nx:run-commands', + options: { + command: `kill-port --port 9099,5001,8080,9000,5000,8085,9199,9299,4000,4400,4500`, + }, + }, + getconfig: { + executor: 'nx:run-commands', + options: { + command: `nx run ${projectName}:firebase functions:config:get > ${projectDir}/environment/.runtimeconfig.json`, + }, + }, + emulate: { + executor: 'nx:run-commands', + options: { + commands: [ + `nx run ${projectName}:killports`, + `nx run ${projectName}:firebase emulators:start --import=${projectDir}/.emulators --export-on-exit`, + ], + parallel: false, + }, + }, + serve: { + executor: 'nx:run-commands', + options: { + commands: [ + `nx run ${projectName}:watch`, + `nx run ${projectName}:emulate`, + ], + }, + }, + deploy: { + executor: 'nx:run-commands', + dependsOn: ['build'], + options: { + command: `nx run ${projectName}:firebase deploy`, + }, + }, + } +} + export function validateProjectConfig(projectDir: string, projectName: string) { const project = readJson( `${projectDir}/project.json`, ) // expect(project.root).toEqual(`apps/${projectName}`) expect(project.targets).toEqual( - expect.objectContaining({ - build: { - executor: 'nx:run-commands', - options: { - command: `echo Build succeeded.`, - }, - }, - watch: { - executor: 'nx:run-commands', - options: { - command: `nx run-many --targets=build --projects=tag:firebase:dep:${projectName} --parallel=100 --watch`, - }, - }, - lint: { - executor: 'nx:run-commands', - options: { - command: `nx run-many --targets=lint --projects=tag:firebase:dep:${projectName} --parallel=100`, - }, - }, - test: { - executor: 'nx:run-commands', - options: { - command: `nx run-many --targets=test --projects=tag:firebase:dep:${projectName} --parallel=100`, - }, - }, - firebase: { - executor: 'nx:run-commands', - options: { - command: `firebase --config=firebase.json`, - }, - configurations: { - production: { - command: `firebase --config=firebase.json`, - }, - }, - }, - killports: { - executor: 'nx:run-commands', - options: { - command: `kill-port --port 9099,5001,8080,9000,5000,8085,9199,9299,4000,4400,4500`, - }, - }, - getconfig: { - executor: 'nx:run-commands', - options: { - command: `nx run ${projectName}:firebase functions:config:get > ${projectDir}/environment/.runtimeconfig.json`, - }, - }, - emulate: { - executor: 'nx:run-commands', - options: { - commands: [ - `nx run ${projectName}:killports`, - `nx run ${projectName}:firebase emulators:start --import=${projectDir}/.emulators --export-on-exit`, - ], - parallel: false, - }, - }, - serve: { - executor: 'nx:run-commands', - options: { - commands: [ - `nx run ${projectName}:watch`, - `nx run ${projectName}:emulate`, - ], - }, - }, - deploy: { - executor: 'nx:run-commands', - dependsOn: ['build'], - options: { - command: `nx run ${projectName}:firebase deploy`, - }, - }, - }), + expect.objectContaining(expectedAppProjectTargets(projectDir, projectName)), ) } + diff --git a/e2e/nx-firebase-e2e/tests/nx-firebase.spec.ts b/e2e/nx-firebase-e2e/tests/nx-firebase.spec.ts index d5ea23ae..92ff570d 100644 --- a/e2e/nx-firebase-e2e/tests/nx-firebase.spec.ts +++ b/e2e/nx-firebase-e2e/tests/nx-firebase.spec.ts @@ -1092,4 +1092,38 @@ describe('nx-firebase e2e', () => { await cleanAppAsync(appData) }) }) + + + //-------------------------------------------------------------------------------------------------- + // Test migrations + //-------------------------------------------------------------------------------------------------- + + // describe('nx-firebase sync migrate', () => { + // it( + // 'should successfuly sync migrate for legacy app', + // async () => { + // const appData = getProjectData('apps', uniq('firebaseMigrateApp')) + // const functionData = getProjectData('apps', uniq('firebaseMigrateFunction')) + // await appGeneratorAsync(appData) + // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + + + + + // const result = await syncGeneratorAsync(`--migrate`) + // testDebug(result.stdout) + // expectStrings(result.stdout, [ + // `CHANGE updating firebase target --project for '${appData.projectName}' to '--project=test2'`, + // `UPDATE apps/${appData.projectName}/project.json`, + // ]) + // expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).toContain( + // `--project=test2` + // ) + + + // // cleanup + // await cleanFunctionAsync(functionData) + // await cleanAppAsync(appData) + // }) + // }) }) diff --git a/packages/nx-firebase/src/generators/sync/lib/index.ts b/packages/nx-firebase/src/generators/sync/lib/index.ts index ee893cbc..ff113caf 100644 --- a/packages/nx-firebase/src/generators/sync/lib/index.ts +++ b/packages/nx-firebase/src/generators/sync/lib/index.ts @@ -5,3 +5,4 @@ export * from './firebase-projects' export * from './firebase-workspace' export * from './tags' export * from './types' +export * from './update-targets' diff --git a/packages/nx-firebase/src/generators/sync/lib/migrate.ts b/packages/nx-firebase/src/generators/sync/lib/migrate.ts new file mode 100644 index 00000000..3e018094 --- /dev/null +++ b/packages/nx-firebase/src/generators/sync/lib/migrate.ts @@ -0,0 +1,90 @@ +import { + Tree, + joinPathFragments, + logger, + updateProjectConfiguration, + writeJson, +} from '@nx/devkit' +import { FirebaseWorkspace } from './types' +import { readFileSync } from 'fs' +import { FirebaseFunction } from '../../../utils' + +export function runMigrations(tree: Tree, workspace: FirebaseWorkspace) { + logger.info(`Running plugin migrations for workspace`) + + // ensure ignores in .gitignore + // ensure ignores in .nxignore + // init generator takes care of this + + // [2.0.0 -> 2.1.0] ensure environment files are present in apps + workspace.firebaseAppProjects.forEach((project, name) => { + const envPath = `${project.root}/environment` + const envFiles = [`.env`, `.env.local`, `.secret.local`] + for (const envFile of envFiles) { + const fullPath = `${envPath}/${envFile}` + if (!tree.exists(fullPath)) { + const src = readFileSync( + joinPathFragments( + __dirname, + '..', + '..', + 'application', + 'files', + 'environment', + envFile, + ), + ) + tree.write(joinPathFragments(project.root, 'environment', envFile), src) + } + } + }) + + // [2.0.0 -> 2.1.0] ensure getconfig path is environment + workspace.firebaseAppProjects.forEach((project, name) => { + const getconfig = project.targets['getconfig'] + const command = getconfig?.options.command as string + if (command) { + getconfig.options.command = command.replace( + joinPathFragments(project.root, '.runtimeconfig.json'), + joinPathFragments(project.root, 'environment', '.runtimeconfig.json'), + ) + updateProjectConfiguration(tree, project.name, project) + } + }) + + // [2.0.0 -> 2.1.0] ensure globs in function projects + workspace.firebaseFunctionProjects.forEach((project, name) => { + const assets = project.targets['build']?.options.assets as string[] + const globs = `{ "glob": "**/*", "input": "${project.root}/environment", "output": "."}` + if (!assets.includes(globs)) { + assets.push(globs) + updateProjectConfiguration(tree, project.name, project) + } + }) + + // [2.0.0 -> 2.1.0] ensure ignores to function firebase.json + workspace.firebaseConfigs.forEach((config, configFilename) => { + if (!Array.isArray(config.functions)) { + // promote to array if single function object + config.functions = [config.functions as FirebaseFunction] + } + config.functions.map((func: FirebaseFunction) => { + const ignoreRule = '*.local' + const ignore = func.ignore || [ignoreRule] + if (!ignore.includes[ignoreRule]) { + ignore.push(ignoreRule) + } + }) + writeJson(tree, configFilename, config) + }) + + // [2.0.0 -> 2.1.0] change firebase serve target + workspace.firebaseAppProjects.forEach((project, name) => { + const serve = project.targets['serve'] + const serveExecutor = '@simondotm/nx-firebase:serve' + if (serve.executor !== serveExecutor) { + serve.executor = serveExecutor + updateProjectConfiguration(tree, project.name, project) + } + }) +} diff --git a/packages/nx-firebase/src/generators/sync/schema.d.ts b/packages/nx-firebase/src/generators/sync/schema.d.ts index 56436e10..0e4e8a2c 100644 --- a/packages/nx-firebase/src/generators/sync/schema.d.ts +++ b/packages/nx-firebase/src/generators/sync/schema.d.ts @@ -2,4 +2,5 @@ export interface SyncGeneratorSchema { app?: string project?: string // functions?: boolean + migrate?: boolean } diff --git a/packages/nx-firebase/src/generators/sync/schema.json b/packages/nx-firebase/src/generators/sync/schema.json index eb4c6c25..56f8b2a5 100644 --- a/packages/nx-firebase/src/generators/sync/schema.json +++ b/packages/nx-firebase/src/generators/sync/schema.json @@ -13,7 +13,12 @@ "type": "string", "description": "The firebase project that should be associated with this application", "default": "" - } + }, + "migrate": { + "type": "boolean", + "description": "Run the plugin migration check for your workspace", + "default": false + } }, "required": [] } diff --git a/packages/nx-firebase/src/generators/sync/sync.ts b/packages/nx-firebase/src/generators/sync/sync.ts index 4a5ce4ff..a1c67fdb 100644 --- a/packages/nx-firebase/src/generators/sync/sync.ts +++ b/packages/nx-firebase/src/generators/sync/sync.ts @@ -20,8 +20,9 @@ import { CONFIG_NO_APP, updateFirebaseProjectNameTag, getFirebaseWorkspace, + renameCommandForTarget, } from './lib' -import { renameCommandForTarget } from './lib/update-targets' +import { runMigrations } from './lib/migrate' const FUNCTIONS_DEPLOY_MATCHER = /(--only[ =]functions:)([^\s]+)/ @@ -39,6 +40,22 @@ export async function syncGenerator( const initTask = await initGenerator(tree, {}) tasks.push(initTask) + // otherwise, sync the workspace. + // build lists of firebase apps & functions that have been deleted or renamed + debugInfo('- Syncing workspace') + + const workspace = getFirebaseWorkspace(tree) + + logger.info( + `This workspace has ${workspace.firebaseAppProjects.size} firebase apps and ${workspace.firebaseFunctionProjects.size} firebase functions\n\n`, + ) + + // run migrations if required + if (options.migrate) { + runMigrations(tree, workspace) + return + } + // change the firebase project for an nx firebase app project if (options.project) { // --project option requires --app option to be specified @@ -57,16 +74,6 @@ export async function syncGenerator( return } - // otherwise, sync the workspace. - // build lists of firebase apps & functions that have been deleted or renamed - debugInfo('- Syncing workspace') - - const workspace = getFirebaseWorkspace(tree) - - logger.info( - `This workspace has ${workspace.firebaseAppProjects.size} firebase apps and ${workspace.firebaseFunctionProjects.size} firebase functions\n\n`, - ) - /** * Nx automatically: * - updates implicitDependencies when projects are renamed with `nx g mv` diff --git a/packages/nx-firebase/src/utils/firebase-config.ts b/packages/nx-firebase/src/utils/firebase-config.ts index 30c7f8b3..3a798944 100644 --- a/packages/nx-firebase/src/utils/firebase-config.ts +++ b/packages/nx-firebase/src/utils/firebase-config.ts @@ -7,6 +7,7 @@ export interface FirebaseFunction { source: string codebase: string runtime: string // 'nodejs16' | 'nodejs18' | 'nodejs20' + ignore?: string[] } export interface FirebaseConfig { From 8e2595f48bd19debc7d07f4fb1878a4e63b2b184 Mon Sep 17 00:00:00 2001 From: Simon M Date: Wed, 20 Sep 2023 22:47:02 +0100 Subject: [PATCH 2/8] Migrate updates & tests --- e2e/nx-firebase-e2e/tests/nx-firebase.spec.ts | 1749 +++++++++-------- .../src/generators/sync/lib/migrate.ts | 27 +- 2 files changed, 919 insertions(+), 857 deletions(-) diff --git a/e2e/nx-firebase-e2e/tests/nx-firebase.spec.ts b/e2e/nx-firebase-e2e/tests/nx-firebase.spec.ts index 92ff570d..db4ac502 100644 --- a/e2e/nx-firebase-e2e/tests/nx-firebase.spec.ts +++ b/e2e/nx-firebase-e2e/tests/nx-firebase.spec.ts @@ -7,6 +7,8 @@ import { updateFile, exists, readFile, + getCwd, + renameFile, } from '@nx/plugin/testing' import { @@ -29,6 +31,8 @@ import { safeRunNxCommandAsync, validateProjectConfig, } from '../test-utils' +import { ProjectConfiguration, joinPathFragments, writeJsonFile } from '@nx/devkit' +import { rmSync } from 'fs' const JEST_TIMEOUT = 190000 @@ -165,965 +169,1006 @@ describe('nx-firebase e2e', () => { }) - //-------------------------------------------------------------------------------------------------- - // Create Libraries for e2e function generator tests - //-------------------------------------------------------------------------------------------------- - describe('setup libraries', () => { - it( - 'should create buildable typescript library', - async () => { - await libGeneratorAsync(buildableLibData, `--buildable --importPath="${buildableLibData.npmScope}"`) - - // no need to test the js library generator, only that it ran ok - expect(() => - checkFilesExist(`${buildableLibData.projectDir}/package.json`), - ).not.toThrow() - - const result = await runNxCommandAsync( - `build ${buildableLibData.projectName}`, - ) - expect(result.stdout).toContain(compileComplete) - expect(result.stdout).toContain( - `${buildSuccess} ${buildableLibData.projectName}`, - ) - }) + // //-------------------------------------------------------------------------------------------------- + // // Create Libraries for e2e function generator tests + // //-------------------------------------------------------------------------------------------------- + // describe('setup libraries', () => { + // it( + // 'should create buildable typescript library', + // async () => { + // await libGeneratorAsync(buildableLibData, `--buildable --importPath="${buildableLibData.npmScope}"`) - it( - 'should create buildable typescript library in subdir', - async () => { - await libGeneratorAsync(subDirBuildableLibData, `--directory=${subDirBuildableLibData.dir} --buildable --importPath="${subDirBuildableLibData.npmScope}"`) - - // no need to test the js library generator, only that it ran ok - expect(() => - checkFilesExist(`${subDirBuildableLibData.projectDir}/package.json`), - ).not.toThrow() - - const result = await runNxCommandAsync( - `build ${subDirBuildableLibData.projectName}`, - ) - expect(result.stdout).toContain(compileComplete) - expect(result.stdout).toContain( - `${buildSuccess} ${subDirBuildableLibData.projectName}`, - ) - }) + // // no need to test the js library generator, only that it ran ok + // expect(() => + // checkFilesExist(`${buildableLibData.projectDir}/package.json`), + // ).not.toThrow() - it( - 'should create non-buildable typescript library', - async () => { - await libGeneratorAsync(nonbuildableLibData, `--buildable=false --importPath="${nonbuildableLibData.npmScope}"`) + // const result = await runNxCommandAsync( + // `build ${buildableLibData.projectName}`, + // ) + // expect(result.stdout).toContain(compileComplete) + // expect(result.stdout).toContain( + // `${buildSuccess} ${buildableLibData.projectName}`, + // ) + // }) - expect(() => - checkFilesExist(`${nonbuildableLibData.projectDir}/package.json`), - ).toThrow() + // it( + // 'should create buildable typescript library in subdir', + // async () => { + // await libGeneratorAsync(subDirBuildableLibData, `--directory=${subDirBuildableLibData.dir} --buildable --importPath="${subDirBuildableLibData.npmScope}"`) - const project = readJson( - `${nonbuildableLibData.projectDir}/project.json`, - ) - expect(project.targets.build).not.toBeDefined() - }) + // // no need to test the js library generator, only that it ran ok + // expect(() => + // checkFilesExist(`${subDirBuildableLibData.projectDir}/package.json`), + // ).not.toThrow() - it( - 'should create non-buildable typescript library in subdir', - async () => { - // const projectData = getProjectData('libs', 'nonbuildablelib', { dir: 'subdir' }) - await libGeneratorAsync(subDirNonbuildableLibData, `--directory=${subDirNonbuildableLibData.dir} --buildable=false --importPath="${subDirNonbuildableLibData.npmScope}"`) + // const result = await runNxCommandAsync( + // `build ${subDirBuildableLibData.projectName}`, + // ) + // expect(result.stdout).toContain(compileComplete) + // expect(result.stdout).toContain( + // `${buildSuccess} ${subDirBuildableLibData.projectName}`, + // ) + // }) + + // it( + // 'should create non-buildable typescript library', + // async () => { + // await libGeneratorAsync(nonbuildableLibData, `--buildable=false --importPath="${nonbuildableLibData.npmScope}"`) - expect(() => - checkFilesExist(`${subDirNonbuildableLibData.projectDir}/package.json`), - ).toThrow() + // expect(() => + // checkFilesExist(`${nonbuildableLibData.projectDir}/package.json`), + // ).toThrow() - const project = readJson( - `${subDirNonbuildableLibData.projectDir}/project.json`, - ) - expect(project.targets.build).not.toBeDefined() - }) - }) + // const project = readJson( + // `${nonbuildableLibData.projectDir}/project.json`, + // ) + // expect(project.targets.build).not.toBeDefined() + // }) + + // it( + // 'should create non-buildable typescript library in subdir', + // async () => { + // // const projectData = getProjectData('libs', 'nonbuildablelib', { dir: 'subdir' }) + // await libGeneratorAsync(subDirNonbuildableLibData, `--directory=${subDirNonbuildableLibData.dir} --buildable=false --importPath="${subDirNonbuildableLibData.npmScope}"`) + + // expect(() => + // checkFilesExist(`${subDirNonbuildableLibData.projectDir}/package.json`), + // ).toThrow() + + // const project = readJson( + // `${subDirNonbuildableLibData.projectDir}/project.json`, + // ) + // expect(project.targets.build).not.toBeDefined() + // }) + // }) - //-------------------------------------------------------------------------------------------------- - // Application generator e2e tests - //-------------------------------------------------------------------------------------------------- + // //-------------------------------------------------------------------------------------------------- + // // Application generator e2e tests + // //-------------------------------------------------------------------------------------------------- - describe('nx-firebase application', () => { + // describe('nx-firebase application', () => { - it( - 'should create nx-firebase app', - async () => { - const appData = getProjectData('apps', uniq('firebaseSetupApp')) - await appGeneratorAsync(appData) - // test generator output - expect(() => - checkFilesExist( - ...expectedAppFiles(appData), - ), - ).not.toThrow() + // it( + // 'should create nx-firebase app', + // async () => { + // const appData = getProjectData('apps', uniq('firebaseSetupApp')) + // await appGeneratorAsync(appData) + // // test generator output + // expect(() => + // checkFilesExist( + // ...expectedAppFiles(appData), + // ), + // ).not.toThrow() - validateProjectConfig(appData.projectDir, appData.projectName) + // validateProjectConfig(appData.projectDir, appData.projectName) - // cleanup - app - await cleanAppAsync(appData) - }) + // // cleanup - app + // await cleanAppAsync(appData) + // }) - it( - 'should build nx-firebase app', - async () => { - const appData = getProjectData('apps', uniq('firebaseSetupApp')) - await appGeneratorAsync(appData) + // it( + // 'should build nx-firebase app', + // async () => { + // const appData = getProjectData('apps', uniq('firebaseSetupApp')) + // await appGeneratorAsync(appData) - // test app builder - // at this point there are no functions so it doe nothing - const result = await runNxCommandAsync(`build ${appData.projectName}`) - expect(result.stdout).toContain("Build succeeded.") + // // test app builder + // // at this point there are no functions so it doe nothing + // const result = await runNxCommandAsync(`build ${appData.projectName}`) + // expect(result.stdout).toContain("Build succeeded.") - // cleanup - app - await cleanAppAsync(appData) - }) + // // cleanup - app + // await cleanAppAsync(appData) + // }) - describe('--directory', () => { - it( - 'should create nx-firebase app in the specified directory', - async () => { - const appData = getProjectData('apps', uniq('firebaseSetupApp'), { dir: 'subdir' }) - await appGeneratorAsync(appData, - `--directory ${appData.dir}`, - ) - expect(() => - checkFilesExist( - ...expectedAppFiles(appData), - ), - ).not.toThrow() - - const project = readJson(`${appData.projectDir}/project.json`) - expect(project.name).toEqual(`${appData.projectName}`) - - validateProjectConfig(appData.projectDir, appData.projectName) - - // cleanup - app - await cleanAppAsync(appData) - }) - }) + // describe('--directory', () => { + // it( + // 'should create nx-firebase app in the specified directory', + // async () => { + // const appData = getProjectData('apps', uniq('firebaseSetupApp'), { dir: 'subdir' }) + // await appGeneratorAsync(appData, + // `--directory ${appData.dir}`, + // ) + // expect(() => + // checkFilesExist( + // ...expectedAppFiles(appData), + // ), + // ).not.toThrow() + + // const project = readJson(`${appData.projectDir}/project.json`) + // expect(project.name).toEqual(`${appData.projectName}`) + + // validateProjectConfig(appData.projectDir, appData.projectName) + + // // cleanup - app + // await cleanAppAsync(appData) + // }) + // }) - describe('--tags', () => { - it( - 'should add tags to the project', - async () => { - const appData = getProjectData('apps', uniq('firebaseSetupApp')) - await appGeneratorAsync(appData, - `--tags e2etag,e2ePackage`, - ) - const project = readJson(`${appData.projectDir}/project.json`) - expect(project.tags).toEqual(['firebase:app', `firebase:name:${appData.projectName}`, 'e2etag', 'e2ePackage']) - - // cleanup - app - await cleanAppAsync(appData) - } + // describe('--tags', () => { + // it( + // 'should add tags to the project', + // async () => { + // const appData = getProjectData('apps', uniq('firebaseSetupApp')) + // await appGeneratorAsync(appData, + // `--tags e2etag,e2ePackage`, + // ) + // const project = readJson(`${appData.projectDir}/project.json`) + // expect(project.tags).toEqual(['firebase:app', `firebase:name:${appData.projectName}`, 'e2etag', 'e2ePackage']) + + // // cleanup - app + // await cleanAppAsync(appData) + // } - ) - }) - }) + // ) + // }) + // }) - //-------------------------------------------------------------------------------------------------- - // Function generator e2e tests - //-------------------------------------------------------------------------------------------------- + // //-------------------------------------------------------------------------------------------------- + // // Function generator e2e tests + // //-------------------------------------------------------------------------------------------------- - describe('nx-firebase function', () => { - it( - 'should not create nx-firebase function without --app', - async () => { - const functionData = getProjectData('apps', uniq('firebaseFunction')) - const result = await functionGeneratorAsync(functionData) - expect(result.stdout).toContain("Required property 'app' is missing") - // no cleanup required - }) + // describe('nx-firebase function', () => { + // it( + // 'should not create nx-firebase function without --app', + // async () => { + // const functionData = getProjectData('apps', uniq('firebaseFunction')) + // const result = await functionGeneratorAsync(functionData) + // expect(result.stdout).toContain("Required property 'app' is missing") + // // no cleanup required + // }) - it( - 'should not create nx-firebase function with an invalid --app', - async () => { - const functionData = getProjectData('apps', uniq('firebaseFunction')) - const result = await functionGeneratorAsync(functionData, '--app badapple') - expect(result.stdout).toContain("A firebase application project called 'badapple' was not found in this workspace.") - // no cleanup required - }) + // it( + // 'should not create nx-firebase function with an invalid --app', + // async () => { + // const functionData = getProjectData('apps', uniq('firebaseFunction')) + // const result = await functionGeneratorAsync(functionData, '--app badapple') + // expect(result.stdout).toContain("A firebase application project called 'badapple' was not found in this workspace.") + // // no cleanup required + // }) - it( - 'should create nx-firebase function', - async () => { - const appData = getProjectData('apps', uniq('firebaseApp')) - const functionData = getProjectData('apps', uniq('firebaseFunction')) - await appGeneratorAsync(appData) - await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - // test generator output - expect(() => - checkFilesExist( - ...expectedFunctionFiles(functionData) - ), - ).not.toThrow() - - // check dist files dont exist and we havent accidentally run this test out of sequence - expect(() => - checkFilesExist( - `dist/${functionData.projectDir}/main.js`, - `dist/${functionData.projectDir}/package.json`, - `dist/${functionData.projectDir}/.env`, - `dist/${functionData.projectDir}/.env.local`, - `dist/${functionData.projectDir}/.secret.local`, - ), - ).toThrow() + // it( + // 'should create nx-firebase function', + // async () => { + // const appData = getProjectData('apps', uniq('firebaseApp')) + // const functionData = getProjectData('apps', uniq('firebaseFunction')) + // await appGeneratorAsync(appData) + // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + // // test generator output + // expect(() => + // checkFilesExist( + // ...expectedFunctionFiles(functionData) + // ), + // ).not.toThrow() + + // // check dist files dont exist and we havent accidentally run this test out of sequence + // expect(() => + // checkFilesExist( + // `dist/${functionData.projectDir}/main.js`, + // `dist/${functionData.projectDir}/package.json`, + // `dist/${functionData.projectDir}/.env`, + // `dist/${functionData.projectDir}/.env.local`, + // `dist/${functionData.projectDir}/.secret.local`, + // ), + // ).toThrow() - // cleanup - await cleanFunctionAsync(functionData) - await cleanAppAsync(appData) - }) + // // cleanup + // await cleanFunctionAsync(functionData) + // await cleanAppAsync(appData) + // }) - it( - 'should build nx-firebase function from the app', - async () => { - const appData = getProjectData('apps', uniq('firebaseApp')) - const functionData = getProjectData('apps', uniq('firebaseFunction')) - await appGeneratorAsync(appData) - await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + // it( + // 'should build nx-firebase function from the app', + // async () => { + // const appData = getProjectData('apps', uniq('firebaseApp')) + // const functionData = getProjectData('apps', uniq('firebaseFunction')) + // await appGeneratorAsync(appData) + // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - const result = await runTargetAsync(appData, 'build') - expect(result.stdout).toContain("Build succeeded.") - - expect(() => - checkFilesExist( - `dist/${functionData.projectDir}/main.js`, - `dist/${functionData.projectDir}/package.json`, - `dist/${functionData.projectDir}/.env`, - `dist/${functionData.projectDir}/.env.local`, - `dist/${functionData.projectDir}/.secret.local`, - ), - ).not.toThrow() + // const result = await runTargetAsync(appData, 'build') + // expect(result.stdout).toContain("Build succeeded.") + + // expect(() => + // checkFilesExist( + // `dist/${functionData.projectDir}/main.js`, + // `dist/${functionData.projectDir}/package.json`, + // `dist/${functionData.projectDir}/.env`, + // `dist/${functionData.projectDir}/.env.local`, + // `dist/${functionData.projectDir}/.secret.local`, + // ), + // ).not.toThrow() - // cleanup - await cleanFunctionAsync(functionData) - await cleanAppAsync(appData) - }) + // // cleanup + // await cleanFunctionAsync(functionData) + // await cleanAppAsync(appData) + // }) - it( - 'should build nx-firebase function directly', - async () => { - const appData = getProjectData('apps', uniq('firebaseApp')) - const functionData = getProjectData('apps', uniq('firebaseFunction')) - await appGeneratorAsync(appData) - await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + // it( + // 'should build nx-firebase function directly', + // async () => { + // const appData = getProjectData('apps', uniq('firebaseApp')) + // const functionData = getProjectData('apps', uniq('firebaseFunction')) + // await appGeneratorAsync(appData) + // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - const result = await runTargetAsync(functionData, 'build') - expect(result.stdout).toContain(`nx run ${functionData.projectName}:build`) - // esbuild outputs to stderr for some reason - expect(result.stderr).toContain(`${functionData.distDir}/main.js`) - // make sure it hasnt bundled node_modules, indicator is that bundle size is megabytes in size - expect(result.stderr).not.toContain(`Mb`) + // const result = await runTargetAsync(functionData, 'build') + // expect(result.stdout).toContain(`nx run ${functionData.projectName}:build`) + // // esbuild outputs to stderr for some reason + // expect(result.stderr).toContain(`${functionData.distDir}/main.js`) + // // make sure it hasnt bundled node_modules, indicator is that bundle size is megabytes in size + // expect(result.stderr).not.toContain(`Mb`) - // cleanup - await cleanFunctionAsync(functionData) - await cleanAppAsync(appData) - }) + // // cleanup + // await cleanFunctionAsync(functionData) + // await cleanAppAsync(appData) + // }) - it( - 'should add correct dependencies to the built function package.json', - async () => { - const appData = getProjectData('apps', uniq('firebaseApp')) - const functionData = getProjectData('apps', uniq('firebaseFunction')) - await appGeneratorAsync(appData) - await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + // it( + // 'should add correct dependencies to the built function package.json', + // async () => { + // const appData = getProjectData('apps', uniq('firebaseApp')) + // const functionData = getProjectData('apps', uniq('firebaseFunction')) + // await appGeneratorAsync(appData) + // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - const result = await runTargetAsync(functionData, 'build') - expect(result.stdout).toContain( - `Successfully ran target build for project ${functionData.projectName}`, - ) + // const result = await runTargetAsync(functionData, 'build') + // expect(result.stdout).toContain( + // `Successfully ran target build for project ${functionData.projectName}`, + // ) - expectStrings(result.stderr, [ - `${functionData.distDir}/main.js` - ]) - // make sure output build is not megabytes in size, which would mean we've - // bundled node_modules as well - expect(result.stdout).not.toContain('Mb') + // expectStrings(result.stderr, [ + // `${functionData.distDir}/main.js` + // ]) + // // make sure output build is not megabytes in size, which would mean we've + // // bundled node_modules as well + // expect(result.stdout).not.toContain('Mb') - const distPackageFile = `${functionData.distDir}/package.json` - expect(exists(distPackageFile)) + // const distPackageFile = `${functionData.distDir}/package.json` + // expect(exists(distPackageFile)) - const distPackage = readJson(distPackageFile) - const deps = distPackage['dependencies'] - expect(deps).toBeDefined() - // firebase-admin is No longer in the default main.ts template - // expect(deps['firebase-admin']).toBeDefined() - expect(deps['firebase-functions']).toBeDefined() + // const distPackage = readJson(distPackageFile) + // const deps = distPackage['dependencies'] + // expect(deps).toBeDefined() + // // firebase-admin is No longer in the default main.ts template + // // expect(deps['firebase-admin']).toBeDefined() + // expect(deps['firebase-functions']).toBeDefined() - // cleanup - await cleanFunctionAsync(functionData) - await cleanAppAsync(appData) - }) + // // cleanup + // await cleanFunctionAsync(functionData) + // await cleanAppAsync(appData) + // }) - it( - 'should add tags to the function project', - async () => { - const appData = getProjectData('apps', uniq('firebaseApp')) - const functionData = getProjectData('apps', uniq('firebaseFunction')) - await appGeneratorAsync(appData) - await functionGeneratorAsync(functionData, `--app ${appData.projectName} --tags e2etag,e2ePackage`) - - const project = readJson(`${functionData.projectDir}/project.json`) - expect(project.tags).toEqual([ - 'firebase:function', - `firebase:name:${functionData.projectName}`, - `firebase:dep:${appData.projectName}`, - 'e2etag', - 'e2ePackage', - ]) + // it( + // 'should add tags to the function project', + // async () => { + // const appData = getProjectData('apps', uniq('firebaseApp')) + // const functionData = getProjectData('apps', uniq('firebaseFunction')) + // await appGeneratorAsync(appData) + // await functionGeneratorAsync(functionData, `--app ${appData.projectName} --tags e2etag,e2ePackage`) + + // const project = readJson(`${functionData.projectDir}/project.json`) + // expect(project.tags).toEqual([ + // 'firebase:function', + // `firebase:name:${functionData.projectName}`, + // `firebase:dep:${appData.projectName}`, + // 'e2etag', + // 'e2ePackage', + // ]) - // cleanup - await cleanFunctionAsync(functionData) - await cleanAppAsync(appData) - } - ) - }) + // // cleanup + // await cleanFunctionAsync(functionData) + // await cleanAppAsync(appData) + // } + // ) + // }) - //-------------------------------------------------------------------------------------------------- - // Test import & dependency handling - //-------------------------------------------------------------------------------------------------- + // //-------------------------------------------------------------------------------------------------- + // // Test import & dependency handling + // //-------------------------------------------------------------------------------------------------- - describe('nx-firebase bundle dependencies', () => { - it( - 'should inline library dependencies into function bundle', - async () => { - // use libs we generated earler + // describe('nx-firebase bundle dependencies', () => { + // it( + // 'should inline library dependencies into function bundle', + // async () => { + // // use libs we generated earler - const appData = getProjectData('apps', uniq('firebaseDepsApp')) - const functionData = getProjectData('apps', uniq('firebaseDepsFunction')) - await appGeneratorAsync(appData) - await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + // const appData = getProjectData('apps', uniq('firebaseDepsApp')) + // const functionData = getProjectData('apps', uniq('firebaseDepsFunction')) + // await appGeneratorAsync(appData) + // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - // add buildable & nonbuildable lib dependencies using import statements - let mainTs = getMainTs() + // // add buildable & nonbuildable lib dependencies using import statements + // let mainTs = getMainTs() - // import from a buildable lib - const libImport1 = getLibImport(buildableLibData) - const importAddition1 = `import { ${libImport1} } from '${buildableLibData.npmScope}'\nconsole.log(${libImport1}())\n` - mainTs = addImport(mainTs, importAddition1) + // // import from a buildable lib + // const libImport1 = getLibImport(buildableLibData) + // const importAddition1 = `import { ${libImport1} } from '${buildableLibData.npmScope}'\nconsole.log(${libImport1}())\n` + // mainTs = addImport(mainTs, importAddition1) - // import from a non buildable lib - const libImport2 = getLibImport(nonbuildableLibData) - const importAddition2 = `import { ${libImport2} } from '${nonbuildableLibData.npmScope}'\nconsole.log(${libImport2}())\n` - mainTs = addImport(mainTs, importAddition2) + // // import from a non buildable lib + // const libImport2 = getLibImport(nonbuildableLibData) + // const importAddition2 = `import { ${libImport2} } from '${nonbuildableLibData.npmScope}'\nconsole.log(${libImport2}())\n` + // mainTs = addImport(mainTs, importAddition2) - // import from a buildable subdir lib - const libImport3 = getLibImport(subDirBuildableLibData) - const importAddition3 = `import { ${libImport3} } from '${subDirBuildableLibData.npmScope}'\nconsole.log(${libImport3}())\n` - mainTs = addImport(mainTs, importAddition3) + // // import from a buildable subdir lib + // const libImport3 = getLibImport(subDirBuildableLibData) + // const importAddition3 = `import { ${libImport3} } from '${subDirBuildableLibData.npmScope}'\nconsole.log(${libImport3}())\n` + // mainTs = addImport(mainTs, importAddition3) - // import from a non buildable subdir lib - const libImport4 = getLibImport(subDirNonbuildableLibData) - const importAddition4 = `import { ${libImport4} } from '${subDirNonbuildableLibData.npmScope}'\nconsole.log(${libImport4}())\n` - mainTs = addImport(mainTs, importAddition4) - - // write the new main.ts - updateFile(functionData.mainTsPath, (content: string) => { - return mainTs - }) - - - // confirm the file changes - const updatedMainTs = readFile(functionData.mainTsPath) - expect(updatedMainTs).toContain(importAddition1) - expect(updatedMainTs).toContain(importAddition2) - expect(updatedMainTs).toContain(importAddition3) - expect(updatedMainTs).toContain(importAddition4) - - // build - const result = await runTargetAsync(functionData, `build`) - // check console output - expectStrings(result.stdout, [ - `Running target build for project ${functionData.projectName}`, - `nx run ${buildableLibData.projectName}:build`, - `nx run ${subDirBuildableLibData.projectName}:build`, - `Compiling TypeScript files for project "${subDirBuildableLibData.projectName}"`, - `Compiling TypeScript files for project "${buildableLibData.projectName}"`, - `Done compiling TypeScript files for project "${buildableLibData.projectName}"`, - `Done compiling TypeScript files for project "${subDirBuildableLibData.projectName}"`, - `nx run ${functionData.projectName}:build`, - `Successfully ran target build for project ${functionData.projectName}`, - ]) - expectStrings(result.stderr, [ - `${functionData.distDir}/main.js` - ]) - // make sure output build is not megabytes in size, which would mean we've - // bundled node_modules as well - expect(result.stdout).not.toContain('Mb') - - // check dist outputs - expect(() => - checkFilesExist( - `${functionData.distDir}/package.json`, - `${functionData.distDir}/main.js`, - `${functionData.distDir}/.env`, - `${functionData.distDir}/.env.local`, - `${functionData.distDir}/.secret.local`, - ), - ).not.toThrow() - - // check dist package contains external imports - const distPackage = readJson(`${functionData.distDir}/package.json`) - const deps = distPackage['dependencies'] - expect(deps).toBeDefined() - // firebase-admin not in the template anymore - // expect(deps['firebase-admin']).toBeDefined() - expect(deps['firebase-functions']).toBeDefined() - - // check bundled code contains the libcode we added - const bundle = readFile(`${functionData.distDir}/main.js`) - - // check that node modules were not bundled, happens in e2e if nx reset not called - // probably the earlier check for deps in the package.json already detects this scenario too - expect(bundle).not.toContain(`require_firebase_app`) - - // our imported lib modules should be inlined in the bundle - expect(bundle).toContain(`function ${libImport1}`) - expect(bundle).toContain(`return "${buildableLibData.projectName}"`) - expect(bundle).toContain(`function ${libImport2}`) - expect(bundle).toContain(`return "${nonbuildableLibData.projectName}"`) - expect(bundle).toContain(`function ${libImport3}`) - expect(bundle).toContain(`return "${subDirBuildableLibData.projectName}"`) - expect(bundle).toContain(`function ${libImport4}`) - expect(bundle).toContain(`return "${subDirNonbuildableLibData.projectName}"`) + // // import from a non buildable subdir lib + // const libImport4 = getLibImport(subDirNonbuildableLibData) + // const importAddition4 = `import { ${libImport4} } from '${subDirNonbuildableLibData.npmScope}'\nconsole.log(${libImport4}())\n` + // mainTs = addImport(mainTs, importAddition4) + + // // write the new main.ts + // updateFile(functionData.mainTsPath, (content: string) => { + // return mainTs + // }) + + + // // confirm the file changes + // const updatedMainTs = readFile(functionData.mainTsPath) + // expect(updatedMainTs).toContain(importAddition1) + // expect(updatedMainTs).toContain(importAddition2) + // expect(updatedMainTs).toContain(importAddition3) + // expect(updatedMainTs).toContain(importAddition4) + + // // build + // const result = await runTargetAsync(functionData, `build`) + // // check console output + // expectStrings(result.stdout, [ + // `Running target build for project ${functionData.projectName}`, + // `nx run ${buildableLibData.projectName}:build`, + // `nx run ${subDirBuildableLibData.projectName}:build`, + // `Compiling TypeScript files for project "${subDirBuildableLibData.projectName}"`, + // `Compiling TypeScript files for project "${buildableLibData.projectName}"`, + // `Done compiling TypeScript files for project "${buildableLibData.projectName}"`, + // `Done compiling TypeScript files for project "${subDirBuildableLibData.projectName}"`, + // `nx run ${functionData.projectName}:build`, + // `Successfully ran target build for project ${functionData.projectName}`, + // ]) + // expectStrings(result.stderr, [ + // `${functionData.distDir}/main.js` + // ]) + // // make sure output build is not megabytes in size, which would mean we've + // // bundled node_modules as well + // expect(result.stdout).not.toContain('Mb') + + // // check dist outputs + // expect(() => + // checkFilesExist( + // `${functionData.distDir}/package.json`, + // `${functionData.distDir}/main.js`, + // `${functionData.distDir}/.env`, + // `${functionData.distDir}/.env.local`, + // `${functionData.distDir}/.secret.local`, + // ), + // ).not.toThrow() + + // // check dist package contains external imports + // const distPackage = readJson(`${functionData.distDir}/package.json`) + // const deps = distPackage['dependencies'] + // expect(deps).toBeDefined() + // // firebase-admin not in the template anymore + // // expect(deps['firebase-admin']).toBeDefined() + // expect(deps['firebase-functions']).toBeDefined() + + // // check bundled code contains the libcode we added + // const bundle = readFile(`${functionData.distDir}/main.js`) + + // // check that node modules were not bundled, happens in e2e if nx reset not called + // // probably the earlier check for deps in the package.json already detects this scenario too + // expect(bundle).not.toContain(`require_firebase_app`) + + // // our imported lib modules should be inlined in the bundle + // expect(bundle).toContain(`function ${libImport1}`) + // expect(bundle).toContain(`return "${buildableLibData.projectName}"`) + // expect(bundle).toContain(`function ${libImport2}`) + // expect(bundle).toContain(`return "${nonbuildableLibData.projectName}"`) + // expect(bundle).toContain(`function ${libImport3}`) + // expect(bundle).toContain(`return "${subDirBuildableLibData.projectName}"`) + // expect(bundle).toContain(`function ${libImport4}`) + // expect(bundle).toContain(`return "${subDirNonbuildableLibData.projectName}"`) - // cleanup - await cleanFunctionAsync(functionData) - await cleanAppAsync(appData) - }) + // // cleanup + // await cleanFunctionAsync(functionData) + // await cleanAppAsync(appData) + // }) - }) + // }) - // test the nx-firebase sync generator - describe('nx-firebase sync', () => { + // // test the nx-firebase sync generator + // describe('nx-firebase sync', () => { - it( - 'should sync firebase workspace with no changes', - async () => { - const result = await syncGeneratorAsync() - testDebug(result.stdout) - expect(result.stdout).not.toContain('CHANGE') - expect(result.stdout).not.toContain('UPDATE') - expect(result.stdout).not.toContain('CREATE') - expect(result.stdout).not.toContain('DELETE') - }) + // it( + // 'should sync firebase workspace with no changes', + // async () => { + // const result = await syncGeneratorAsync() + // testDebug(result.stdout) + // expect(result.stdout).not.toContain('CHANGE') + // expect(result.stdout).not.toContain('UPDATE') + // expect(result.stdout).not.toContain('CREATE') + // expect(result.stdout).not.toContain('DELETE') + // }) - describe('--project', () => { - it( - 'should set firebase app project using --project', - async () => { - // create firebase app without specifying firebase deploy --project - const appData = getProjectData('apps', uniq('firebaseSyncApp')) - await appGeneratorAsync(appData) - - expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).not.toContain( - `--project` - ) - const result = await syncGeneratorAsync(`--app=${appData.projectName} --project=test`) - testDebug(result.stdout) - expectStrings(result.stdout, [ - `CHANGE setting firebase target --project for '${appData.projectName}' to '--project=test'`, - `UPDATE apps/${appData.projectName}/project.json`, - ]) - expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).toContain( - `--project=test` - ) - // cleanup - app - await cleanAppAsync(appData) - }) - - it( - 'should update firebase app project using --project', - async () => { + // describe('--project', () => { + // it( + // 'should set firebase app project using --project', + // async () => { + // // create firebase app without specifying firebase deploy --project + // const appData = getProjectData('apps', uniq('firebaseSyncApp')) + // await appGeneratorAsync(appData) + + // expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).not.toContain( + // `--project` + // ) + // const result = await syncGeneratorAsync(`--app=${appData.projectName} --project=test`) + // testDebug(result.stdout) + // expectStrings(result.stdout, [ + // `CHANGE setting firebase target --project for '${appData.projectName}' to '--project=test'`, + // `UPDATE apps/${appData.projectName}/project.json`, + // ]) + // expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).toContain( + // `--project=test` + // ) + // // cleanup - app + // await cleanAppAsync(appData) + // }) + + // it( + // 'should update firebase app project using --project', + // async () => { - // create firebase app specifying firebase deploy --project - const appData = getProjectData('apps', uniq('firebaseSyncApp')) - await appGeneratorAsync(appData, `--project=test`) - - expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).toContain( - `--project=test` - ) - const result = await syncGeneratorAsync(`--app=${appData.projectName} --project=test2`) - testDebug(result.stdout) - expectStrings(result.stdout, [ - `CHANGE updating firebase target --project for '${appData.projectName}' to '--project=test2'`, - `UPDATE apps/${appData.projectName}/project.json`, - ]) - expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).toContain( - `--project=test2` - ) - - // cleanup - app - await cleanAppAsync(appData) - }) - }) + // // create firebase app specifying firebase deploy --project + // const appData = getProjectData('apps', uniq('firebaseSyncApp')) + // await appGeneratorAsync(appData, `--project=test`) + + // expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).toContain( + // `--project=test` + // ) + // const result = await syncGeneratorAsync(`--app=${appData.projectName} --project=test2`) + // testDebug(result.stdout) + // expectStrings(result.stdout, [ + // `CHANGE updating firebase target --project for '${appData.projectName}' to '--project=test2'`, + // `UPDATE apps/${appData.projectName}/project.json`, + // ]) + // expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).toContain( + // `--project=test2` + // ) + + // // cleanup - app + // await cleanAppAsync(appData) + // }) + // }) - describe('deletions', () => { - - it( - 'should detect deleted firebase functions', - async () => { - const appData = getProjectData('apps', uniq('firebaseSyncApp')) - const functionData = getProjectData('apps', uniq('firebaseSyncFunction')) - await appGeneratorAsync(appData) - await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - - await removeProjectAsync(functionData) - - const result = await syncGeneratorAsync() - testDebug(result.stdout) - expectStrings(result.stdout, [ - `CHANGE Firebase function '${functionData.projectName}' was deleted, removing function codebase from '${appData.configName}'`, - `UPDATE ${appData.configName}`, - ]) - - // cleanup - app only, already removed function - await cleanAppAsync(appData) - }) - - it( - 'should detect deleted firebase apps', - async () => { - const appData = getProjectData('apps', uniq('firebaseSyncApp')) - const functionData = getProjectData('apps', uniq('firebaseSyncFunction')) - await appGeneratorAsync(appData) - await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - - await removeProjectAsync(appData) - - const result = await syncGeneratorAsync() - testDebug(result.stdout) - expectStrings(result.stdout, [ - `DELETE ${appData.configName}`, - ]) - expectStrings(result.stderr, [ - `CHANGE Firebase app '${appData.projectName}' was deleted, firebase:dep tag for firebase function '${functionData.projectName}' is no longer linked to a Firebase app.`, - ]) + // describe('deletions', () => { + + // it( + // 'should detect deleted firebase functions', + // async () => { + // const appData = getProjectData('apps', uniq('firebaseSyncApp')) + // const functionData = getProjectData('apps', uniq('firebaseSyncFunction')) + // await appGeneratorAsync(appData) + // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + + // await removeProjectAsync(functionData) + + // const result = await syncGeneratorAsync() + // testDebug(result.stdout) + // expectStrings(result.stdout, [ + // `CHANGE Firebase function '${functionData.projectName}' was deleted, removing function codebase from '${appData.configName}'`, + // `UPDATE ${appData.configName}`, + // ]) + + // // cleanup - app only, already removed function + // await cleanAppAsync(appData) + // }) + + // it( + // 'should detect deleted firebase apps', + // async () => { + // const appData = getProjectData('apps', uniq('firebaseSyncApp')) + // const functionData = getProjectData('apps', uniq('firebaseSyncFunction')) + // await appGeneratorAsync(appData) + // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + + // await removeProjectAsync(appData) + + // const result = await syncGeneratorAsync() + // testDebug(result.stdout) + // expectStrings(result.stdout, [ + // `DELETE ${appData.configName}`, + // ]) + // expectStrings(result.stderr, [ + // `CHANGE Firebase app '${appData.projectName}' was deleted, firebase:dep tag for firebase function '${functionData.projectName}' is no longer linked to a Firebase app.`, + // ]) - // NOTE: - // a deleted firebase app means the assets glob input dir in a function is no longer valid - // this is ok though because Nx quietly fails when processing assets for an invalid input dir - - // cleanup - function only, already removed app - await cleanFunctionAsync(functionData) - }) - - it( - 'should warn when no firebase apps use firebase.json config', - async () => { - const appData = getProjectData('apps', uniq('firebaseSyncApp')) - const appData2 = getProjectData('apps', uniq('firebaseSyncApp'), {customConfig: true}) - await appGeneratorAsync(appData) - await appGeneratorAsync(appData2) - - // delete the app that used firebase.json - await removeProjectAsync(appData) - - const result = await syncGeneratorAsync() - testDebug(result.stdout) - expectStrings(result.stderr, [ - `None of the Firebase apps in this workspace use 'firebase.json' as their config. Firebase CLI may not work as expected. This can be fixed by renaming the config for one of your firebase projects to 'firebase.json'.`, - ]) + // // NOTE: + // // a deleted firebase app means the assets glob input dir in a function is no longer valid + // // this is ok though because Nx quietly fails when processing assets for an invalid input dir + + // // cleanup - function only, already removed app + // await cleanFunctionAsync(functionData) + // }) + + // it( + // 'should warn when no firebase apps use firebase.json config', + // async () => { + // const appData = getProjectData('apps', uniq('firebaseSyncApp')) + // const appData2 = getProjectData('apps', uniq('firebaseSyncApp'), {customConfig: true}) + // await appGeneratorAsync(appData) + // await appGeneratorAsync(appData2) + + // // delete the app that used firebase.json + // await removeProjectAsync(appData) + + // const result = await syncGeneratorAsync() + // testDebug(result.stdout) + // expectStrings(result.stderr, [ + // `None of the Firebase apps in this workspace use 'firebase.json' as their config. Firebase CLI may not work as expected. This can be fixed by renaming the config for one of your firebase projects to 'firebase.json'.`, + // ]) - // cleanup - second app - await cleanFunctionAsync(appData2) - }) + // // cleanup - second app + // await cleanFunctionAsync(appData2) + // }) - }) + // }) - describe('renames', () => { + // describe('renames', () => { - it( - 'should detect renamed firebase functions', - async () => { - const appData = getProjectData('apps', uniq('firebaseSyncApp')) - const functionData = getProjectData('apps', uniq('firebaseSyncFunction')) - const renamedFunctionData = getProjectData('apps', uniq('firebaseSyncFunction')) - await appGeneratorAsync(appData) - await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + // it( + // 'should detect renamed firebase functions', + // async () => { + // const appData = getProjectData('apps', uniq('firebaseSyncApp')) + // const functionData = getProjectData('apps', uniq('firebaseSyncFunction')) + // const renamedFunctionData = getProjectData('apps', uniq('firebaseSyncFunction')) + // await appGeneratorAsync(appData) + // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - expect(readJson(`${functionData.projectDir}/project.json`).targets.deploy.options.command).toContain( - `--only functions:${functionData.projectName}` - ) + // expect(readJson(`${functionData.projectDir}/project.json`).targets.deploy.options.command).toContain( + // `--only functions:${functionData.projectName}` + // ) - await renameProjectAsync(functionData, renamedFunctionData) + // await renameProjectAsync(functionData, renamedFunctionData) - const result = await syncGeneratorAsync() - testDebug(result.stdout) + // const result = await syncGeneratorAsync() + // testDebug(result.stdout) - expectStrings(result.stdout, [ - `CHANGE Firebase function '${functionData.projectName}' was renamed to '${renamedFunctionData.projectName}', updated firebase:name tag`, - `CHANGE Firebase function '${functionData.projectName}' was renamed to '${renamedFunctionData.projectName}', updated codebase in '${appData.configName}'`, - `CHANGE Firebase function '${functionData.projectName}' was renamed to '${renamedFunctionData.projectName}', updated deploy target to '--only=functions:${renamedFunctionData.projectName}'`, - `UPDATE apps/${renamedFunctionData.projectName}/project.json`, - `UPDATE ${appData.configName}`, - ]) + // expectStrings(result.stdout, [ + // `CHANGE Firebase function '${functionData.projectName}' was renamed to '${renamedFunctionData.projectName}', updated firebase:name tag`, + // `CHANGE Firebase function '${functionData.projectName}' was renamed to '${renamedFunctionData.projectName}', updated codebase in '${appData.configName}'`, + // `CHANGE Firebase function '${functionData.projectName}' was renamed to '${renamedFunctionData.projectName}', updated deploy target to '--only=functions:${renamedFunctionData.projectName}'`, + // `UPDATE apps/${renamedFunctionData.projectName}/project.json`, + // `UPDATE ${appData.configName}`, + // ]) - expect(readJson(`${renamedFunctionData.projectDir}/project.json`).targets.deploy.options.command).toContain( - `--only functions:${renamedFunctionData.projectName}` - ) + // expect(readJson(`${renamedFunctionData.projectDir}/project.json`).targets.deploy.options.command).toContain( + // `--only functions:${renamedFunctionData.projectName}` + // ) - // cleanup - function, then app - await cleanFunctionAsync(renamedFunctionData) - await cleanAppAsync(appData) - }) - - - it( - 'should detect renamed firebase apps', - async () => { - const appData = getProjectData('apps', uniq('firebaseSyncApp')) - // we will attach two functions to the app for this test - const functionData = getProjectData('apps', uniq('firebaseSyncFunction')) - const functionData2 = getProjectData('apps', uniq('firebaseSyncFunction')) - const renamedAppData = getProjectData('apps', uniq('firebaseSyncApp')) - await appGeneratorAsync(appData) - await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - await functionGeneratorAsync(functionData2, `--app ${appData.projectName}`) - - await renameProjectAsync(appData, renamedAppData) - - const result = await syncGeneratorAsync() - testDebug(result.stdout) - - expectStrings(result.stdout, [ - `CHANGE Firebase app '${appData.projectName}' linked to primary config file was renamed to '${renamedAppData.projectName}', skipping rename of '${renamedAppData.configName}'`, - `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:name tag`, - `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated targets`, - `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:dep tag in firebase function '${functionData.projectName}'`, - `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated environment assets path in firebase function '${functionData.projectName}'`, - `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:dep tag in firebase function '${functionData2.projectName}'`, - `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated environment assets path in firebase function '${functionData2.projectName}'`, - `UPDATE apps/${renamedAppData.projectName}/project.json`, - `UPDATE apps/${functionData.projectName}/project.json`, - ]) - // we should not rename config if it is called firebase.json - expect(result.stdout).not.toContain( - `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', renamed config file to '${renamedAppData.configName}'`, - ) - expect(result.stdout).not.toContain( - `DELETE ${appData.configName}`, - ) - expect(result.stdout).not.toContain( - `CREATE ${renamedAppData.configName}`, - ) - - // check that app project has correct --config setting after rename - expect(readJson(`${renamedAppData.projectDir}/project.json`).targets.firebase.options.command).toContain( - `--config=${renamedAppData.configName}` - ) + // // cleanup - function, then app + // await cleanFunctionAsync(renamedFunctionData) + // await cleanAppAsync(appData) + // }) + + + // it( + // 'should detect renamed firebase apps', + // async () => { + // const appData = getProjectData('apps', uniq('firebaseSyncApp')) + // // we will attach two functions to the app for this test + // const functionData = getProjectData('apps', uniq('firebaseSyncFunction')) + // const functionData2 = getProjectData('apps', uniq('firebaseSyncFunction')) + // const renamedAppData = getProjectData('apps', uniq('firebaseSyncApp')) + // await appGeneratorAsync(appData) + // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + // await functionGeneratorAsync(functionData2, `--app ${appData.projectName}`) + + // await renameProjectAsync(appData, renamedAppData) + + // const result = await syncGeneratorAsync() + // testDebug(result.stdout) + + // expectStrings(result.stdout, [ + // `CHANGE Firebase app '${appData.projectName}' linked to primary config file was renamed to '${renamedAppData.projectName}', skipping rename of '${renamedAppData.configName}'`, + // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:name tag`, + // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated targets`, + // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:dep tag in firebase function '${functionData.projectName}'`, + // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated environment assets path in firebase function '${functionData.projectName}'`, + // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:dep tag in firebase function '${functionData2.projectName}'`, + // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated environment assets path in firebase function '${functionData2.projectName}'`, + // `UPDATE apps/${renamedAppData.projectName}/project.json`, + // `UPDATE apps/${functionData.projectName}/project.json`, + // ]) + // // we should not rename config if it is called firebase.json + // expect(result.stdout).not.toContain( + // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', renamed config file to '${renamedAppData.configName}'`, + // ) + // expect(result.stdout).not.toContain( + // `DELETE ${appData.configName}`, + // ) + // expect(result.stdout).not.toContain( + // `CREATE ${renamedAppData.configName}`, + // ) + + // // check that app project has correct --config setting after rename + // expect(readJson(`${renamedAppData.projectDir}/project.json`).targets.firebase.options.command).toContain( + // `--config=${renamedAppData.configName}` + // ) - // check rename was successful - validateProjectConfig(renamedAppData.projectDir, renamedAppData.projectName) + // // check rename was successful + // validateProjectConfig(renamedAppData.projectDir, renamedAppData.projectName) - // run another sync to check there should be no orphaned functions from an app rename - const result2 = await syncGeneratorAsync() - expect(result2.stderr).not.toContain('is no longer linked to a Firebase app') - expect(result2.stdout).not.toContain('UPDATE') - - // cleanup - function, then app - await cleanFunctionAsync(functionData) - await cleanFunctionAsync(functionData2) - await cleanAppAsync(renamedAppData) - }) + // // run another sync to check there should be no orphaned functions from an app rename + // const result2 = await syncGeneratorAsync() + // expect(result2.stderr).not.toContain('is no longer linked to a Firebase app') + // expect(result2.stdout).not.toContain('UPDATE') + + // // cleanup - function, then app + // await cleanFunctionAsync(functionData) + // await cleanFunctionAsync(functionData2) + // await cleanAppAsync(renamedAppData) + // }) - it( - 'should detect renamed firebase apps & functions', - async () => { - const appData = getProjectData('apps', uniq('firebaseSyncApp')) - const functionData = getProjectData('apps', uniq('firebaseSyncFunction')) - const renamedFunctionData = getProjectData('apps', uniq('firebaseSyncFunction')) - const renamedAppData = getProjectData('apps', uniq('firebaseSyncApp')) - - await appGeneratorAsync(appData) - await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - - // rename app & function - await renameProjectAsync(appData, renamedAppData) - await renameProjectAsync(functionData, renamedFunctionData) - - const result = await syncGeneratorAsync() - testDebug(result.stdout) - - expectStrings(result.stdout, [ - `CHANGE Firebase app '${appData.projectName}' linked to primary config file was renamed to '${renamedAppData.projectName}', skipping rename of '${renamedAppData.configName}'`, - `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:name tag`, - `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated targets`, - `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:dep tag in firebase function '${renamedFunctionData.projectName}'`, - `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated environment assets path in firebase function '${renamedFunctionData.projectName}'`, - `CHANGE Firebase function '${functionData.projectName}' was renamed to '${renamedFunctionData.projectName}', updated firebase:name tag`, - `CHANGE Firebase function '${functionData.projectName}' was renamed to '${renamedFunctionData.projectName}', updated deploy target to '--only=functions:${renamedFunctionData.projectName}'`, - `CHANGE Firebase function '${functionData.projectName}' was renamed to '${renamedFunctionData.projectName}', updated codebase in '${renamedAppData.configName}'`, - `UPDATE apps/${renamedAppData.projectName}/project.json`, - `UPDATE apps/${renamedFunctionData.projectName}/project.json`, - ]) - // we should not rename config if it is called firebase.json - expect(result.stdout).not.toContain( - `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', renamed config file to '${renamedAppData.configName}'`, - ) - expect(result.stdout).not.toContain( - `DELETE ${appData.configName}`, - ) - expect(result.stdout).not.toContain( - `CREATE ${renamedAppData.configName}`, - ) - - // check that app project has correct --config setting after rename - expect(readJson(`${renamedAppData.projectDir}/project.json`).targets.firebase.options.command).toContain( - `--config=${renamedAppData.configName}` - ) - // check that function project has correct --config setting after rename - expect(readJson(`${renamedFunctionData.projectDir}/project.json`).targets.deploy.options.command).toContain( - `--only functions:${renamedFunctionData.projectName}` - ) + // it( + // 'should detect renamed firebase apps & functions', + // async () => { + // const appData = getProjectData('apps', uniq('firebaseSyncApp')) + // const functionData = getProjectData('apps', uniq('firebaseSyncFunction')) + // const renamedFunctionData = getProjectData('apps', uniq('firebaseSyncFunction')) + // const renamedAppData = getProjectData('apps', uniq('firebaseSyncApp')) + + // await appGeneratorAsync(appData) + // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + + // // rename app & function + // await renameProjectAsync(appData, renamedAppData) + // await renameProjectAsync(functionData, renamedFunctionData) + + // const result = await syncGeneratorAsync() + // testDebug(result.stdout) + + // expectStrings(result.stdout, [ + // `CHANGE Firebase app '${appData.projectName}' linked to primary config file was renamed to '${renamedAppData.projectName}', skipping rename of '${renamedAppData.configName}'`, + // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:name tag`, + // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated targets`, + // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:dep tag in firebase function '${renamedFunctionData.projectName}'`, + // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated environment assets path in firebase function '${renamedFunctionData.projectName}'`, + // `CHANGE Firebase function '${functionData.projectName}' was renamed to '${renamedFunctionData.projectName}', updated firebase:name tag`, + // `CHANGE Firebase function '${functionData.projectName}' was renamed to '${renamedFunctionData.projectName}', updated deploy target to '--only=functions:${renamedFunctionData.projectName}'`, + // `CHANGE Firebase function '${functionData.projectName}' was renamed to '${renamedFunctionData.projectName}', updated codebase in '${renamedAppData.configName}'`, + // `UPDATE apps/${renamedAppData.projectName}/project.json`, + // `UPDATE apps/${renamedFunctionData.projectName}/project.json`, + // ]) + // // we should not rename config if it is called firebase.json + // expect(result.stdout).not.toContain( + // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', renamed config file to '${renamedAppData.configName}'`, + // ) + // expect(result.stdout).not.toContain( + // `DELETE ${appData.configName}`, + // ) + // expect(result.stdout).not.toContain( + // `CREATE ${renamedAppData.configName}`, + // ) + + // // check that app project has correct --config setting after rename + // expect(readJson(`${renamedAppData.projectDir}/project.json`).targets.firebase.options.command).toContain( + // `--config=${renamedAppData.configName}` + // ) + // // check that function project has correct --config setting after rename + // expect(readJson(`${renamedFunctionData.projectDir}/project.json`).targets.deploy.options.command).toContain( + // `--only functions:${renamedFunctionData.projectName}` + // ) - // check rename was successful - validateProjectConfig(renamedAppData.projectDir, renamedAppData.projectName) + // // check rename was successful + // validateProjectConfig(renamedAppData.projectDir, renamedAppData.projectName) - // cleanup - function, then app - await cleanFunctionAsync(renamedFunctionData) - await cleanAppAsync(renamedAppData) - }) - - - - it( - 'should rename configs for renamed firebase apps when multiple apps in workspace', - async () => { - expect(!exists('firebase.json')) - - // create first project that will have the primary firebase.json config - const appDataPrimary = getProjectData('apps', uniq('firebaseSyncApp')) - const functionData = getProjectData('apps', uniq('firebaseSyncFunction')) - await appGeneratorAsync(appDataPrimary) - - expect(appDataPrimary.configName).toEqual('firebase.json') - expect(readJson(`${appDataPrimary.projectDir}/project.json`).targets.firebase.options.command).toContain( - `--config=firebase.json` - ) - expect(exists('firebase.json')) - - // generate second app after first app is generated so that first config is detected - const appData = getProjectData('apps', uniq('firebaseSyncApp'), {customConfig: true}) - const renamedAppData = getProjectData('apps', uniq('firebaseSyncApp'), {customConfig: true}) - - expect(appData.configName).not.toEqual('firebase.json') - expect(renamedAppData.configName).not.toEqual('firebase.json') - - await appGeneratorAsync(appData) - await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - - expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).not.toContain( - `--config=firebase.json` - ) - expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).toContain( - `--config=${appData.configName}` - ) - - await renameProjectAsync(appData, renamedAppData) - - const result = await syncGeneratorAsync() - testDebug(result.stdout) - - expectStrings(result.stdout, [ - `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', renamed config file to '${renamedAppData.configName}'`, - `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:name tag`, - `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:dep tag in firebase function '${functionData.projectName}'`, - `UPDATE apps/${renamedAppData.projectName}/project.json`, - `UPDATE apps/${functionData.projectName}/project.json`, - `DELETE ${appData.configName}`, - `CREATE ${renamedAppData.configName}`, - ]) - - // check that app project has correct --config setting after rename - expect(readJson(`${renamedAppData.projectDir}/project.json`).targets.firebase.options.command).toContain( - `--config=${renamedAppData.configName}` - ) + // // cleanup - function, then app + // await cleanFunctionAsync(renamedFunctionData) + // await cleanAppAsync(renamedAppData) + // }) + + + + // it( + // 'should rename configs for renamed firebase apps when multiple apps in workspace', + // async () => { + // expect(!exists('firebase.json')) + + // // create first project that will have the primary firebase.json config + // const appDataPrimary = getProjectData('apps', uniq('firebaseSyncApp')) + // const functionData = getProjectData('apps', uniq('firebaseSyncFunction')) + // await appGeneratorAsync(appDataPrimary) + + // expect(appDataPrimary.configName).toEqual('firebase.json') + // expect(readJson(`${appDataPrimary.projectDir}/project.json`).targets.firebase.options.command).toContain( + // `--config=firebase.json` + // ) + // expect(exists('firebase.json')) + + // // generate second app after first app is generated so that first config is detected + // const appData = getProjectData('apps', uniq('firebaseSyncApp'), {customConfig: true}) + // const renamedAppData = getProjectData('apps', uniq('firebaseSyncApp'), {customConfig: true}) + + // expect(appData.configName).not.toEqual('firebase.json') + // expect(renamedAppData.configName).not.toEqual('firebase.json') + + // await appGeneratorAsync(appData) + // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + + // expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).not.toContain( + // `--config=firebase.json` + // ) + // expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).toContain( + // `--config=${appData.configName}` + // ) + + // await renameProjectAsync(appData, renamedAppData) + + // const result = await syncGeneratorAsync() + // testDebug(result.stdout) + + // expectStrings(result.stdout, [ + // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', renamed config file to '${renamedAppData.configName}'`, + // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:name tag`, + // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:dep tag in firebase function '${functionData.projectName}'`, + // `UPDATE apps/${renamedAppData.projectName}/project.json`, + // `UPDATE apps/${functionData.projectName}/project.json`, + // `DELETE ${appData.configName}`, + // `CREATE ${renamedAppData.configName}`, + // ]) + + // // check that app project has correct --config setting after rename + // expect(readJson(`${renamedAppData.projectDir}/project.json`).targets.firebase.options.command).toContain( + // `--config=${renamedAppData.configName}` + // ) - // run another sync to check there should be no orphaned functions from an app rename - const result2 = await syncGeneratorAsync() - expect(result2.stderr).not.toContain('is no longer linked to a Firebase app') - expect(result2.stdout).not.toContain('UPDATE') + // // run another sync to check there should be no orphaned functions from an app rename + // const result2 = await syncGeneratorAsync() + // expect(result2.stderr).not.toContain('is no longer linked to a Firebase app') + // expect(result2.stdout).not.toContain('UPDATE') - // cleanup - function, then app - await cleanFunctionAsync(functionData) - await cleanAppAsync(renamedAppData, { appsRemaining: 1, functionsRemaining: 0 }) - await cleanAppAsync(appDataPrimary) - }) + // // cleanup - function, then app + // await cleanFunctionAsync(functionData) + // await cleanAppAsync(renamedAppData, { appsRemaining: 1, functionsRemaining: 0 }) + // await cleanAppAsync(appDataPrimary) + // }) - }) + // }) - }) + // }) - //-------------------------------------------------------------------------------------------------- - // Test app targets - //-------------------------------------------------------------------------------------------------- + // //-------------------------------------------------------------------------------------------------- + // // Test app targets + // //-------------------------------------------------------------------------------------------------- - describe('nx-firebase app targets', () => { - it( - 'should run lint target for app', - async () => { - const appData = getProjectData('apps', uniq('firebaseTargetsApp')) - const functionData = getProjectData('apps', uniq('firebaseTargetsFunction')) - const functionData2 = getProjectData('apps', uniq('firebaseTargetsFunction')) - await appGeneratorAsync(appData) - await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - await functionGeneratorAsync(functionData2, `--app ${appData.projectName}`) + // describe('nx-firebase app targets', () => { + // it( + // 'should run lint target for app', + // async () => { + // const appData = getProjectData('apps', uniq('firebaseTargetsApp')) + // const functionData = getProjectData('apps', uniq('firebaseTargetsFunction')) + // const functionData2 = getProjectData('apps', uniq('firebaseTargetsFunction')) + // await appGeneratorAsync(appData) + // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + // await functionGeneratorAsync(functionData2, `--app ${appData.projectName}`) - const result = await runTargetAsync(appData, 'lint') - expectStrings(result.stdout, [ - `nx run ${appData.projectName}:lint`, - `Running target lint for 2 projects`, - `nx run ${functionData.projectName}:lint`, - `nx run ${functionData2.projectName}:lint`, - `All files pass linting`, - `Successfully ran target lint for 2 projects`, - `Successfully ran target lint for project ${appData.projectName}`, - ]) + // const result = await runTargetAsync(appData, 'lint') + // expectStrings(result.stdout, [ + // `nx run ${appData.projectName}:lint`, + // `Running target lint for 2 projects`, + // `nx run ${functionData.projectName}:lint`, + // `nx run ${functionData2.projectName}:lint`, + // `All files pass linting`, + // `Successfully ran target lint for 2 projects`, + // `Successfully ran target lint for project ${appData.projectName}`, + // ]) - // cleanup - await cleanFunctionAsync(functionData2) - await cleanFunctionAsync(functionData) - await cleanAppAsync(appData) - }) + // // cleanup + // await cleanFunctionAsync(functionData2) + // await cleanFunctionAsync(functionData) + // await cleanAppAsync(appData) + // }) - it( - 'should run test target for app', - async () => { - const appData = getProjectData('apps', uniq('firebaseTargetsApp')) - const functionData = getProjectData('apps', uniq('firebaseTargetsFunction')) - const functionData2 = getProjectData('apps', uniq('firebaseTargetsFunction')) - await appGeneratorAsync(appData) - await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - await functionGeneratorAsync(functionData2, `--app ${appData.projectName}`) + // it( + // 'should run test target for app', + // async () => { + // const appData = getProjectData('apps', uniq('firebaseTargetsApp')) + // const functionData = getProjectData('apps', uniq('firebaseTargetsFunction')) + // const functionData2 = getProjectData('apps', uniq('firebaseTargetsFunction')) + // await appGeneratorAsync(appData) + // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + // await functionGeneratorAsync(functionData2, `--app ${appData.projectName}`) - const result = await runTargetAsync(appData, 'test') - expectStrings(result.stdout, [ - `nx run ${appData.projectName}:test`, - `Running target test for 2 projects`, - `nx run ${functionData.projectName}:test`, - `nx run ${functionData2.projectName}:test`, - `Successfully ran target test for 2 projects`, - `Successfully ran target test for project ${appData.projectName}`, - ]) - - // cleanup - await cleanFunctionAsync(functionData2) - await cleanFunctionAsync(functionData) - await cleanAppAsync(appData) - }) - + // const result = await runTargetAsync(appData, 'test') + // expectStrings(result.stdout, [ + // `nx run ${appData.projectName}:test`, + // `Running target test for 2 projects`, + // `nx run ${functionData.projectName}:test`, + // `nx run ${functionData2.projectName}:test`, + // `Successfully ran target test for 2 projects`, + // `Successfully ran target test for project ${appData.projectName}`, + // ]) - it( - 'should run deploy target for app', - async () => { - const appData = getProjectData('apps', uniq('firebaseTargetsApp')) - const functionData = getProjectData('apps', uniq('firebaseDepsFunction')) - const functionData2 = getProjectData('apps', uniq('firebaseDepsFunction')) - await appGeneratorAsync(appData) - await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - await functionGeneratorAsync(functionData2, `--app ${appData.projectName}`) - - // deploy target will fail because theres no firebase project but thats ok - // we cannot e2e a real firebase project setup atm - const result = await runTargetAsync(appData, 'deploy') - expectStrings(result.stdout, [ - `Running target deploy for project ${appData.projectName}`, - `nx run ${appData.projectName}:deploy`, - `nx run ${appData.projectName}:firebase deploy`, - ]) - // build target will also execute, since functions are implicit dep of app - expectStrings(result.stdout, [ - `nx run ${appData.projectName}:build`, - `nx run ${functionData.projectName}:build`, - `nx run ${functionData2.projectName}:build`, - `Build succeeded`, - ]) - expectStrings(result.stderr, [ - `${functionData.distDir}/main.js`, - `${functionData2.distDir}/main.js`, - ]) - // cleanup - await cleanFunctionAsync(functionData2) - await cleanFunctionAsync(functionData) - await cleanAppAsync(appData) - }) + // // cleanup + // await cleanFunctionAsync(functionData2) + // await cleanFunctionAsync(functionData) + // await cleanAppAsync(appData) + // }) - it( - 'should run deploy target for function', - async () => { - const appData = getProjectData('apps', uniq('firebaseTargetsApp')) - const functionData = getProjectData('apps', uniq('firebaseTargetsFunction')) - await appGeneratorAsync(appData) - await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + // it( + // 'should run deploy target for app', + // async () => { + // const appData = getProjectData('apps', uniq('firebaseTargetsApp')) + // const functionData = getProjectData('apps', uniq('firebaseDepsFunction')) + // const functionData2 = getProjectData('apps', uniq('firebaseDepsFunction')) + // await appGeneratorAsync(appData) + // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + // await functionGeneratorAsync(functionData2, `--app ${appData.projectName}`) - const result = await runTargetAsync(functionData, 'deploy') - expectStrings(result.stdout, [ - `Running target deploy for project ${functionData.projectName}`, - `nx run ${appData.projectName}:deploy`, - `nx run ${appData.projectName}:firebase deploy`, - ]) - // build target will also execute, since functions are implicit dep of app - expectStrings(result.stdout, [ - `nx run ${functionData.projectName}:build`, - `Build succeeded`, - ]) - expectStrings(result.stderr, [ - `${functionData.distDir}/main.js`, - ]) - - // cleanup - await cleanFunctionAsync(functionData) - await cleanAppAsync(appData) - }) - }) - + // // deploy target will fail because theres no firebase project but thats ok + // // we cannot e2e a real firebase project setup atm + // const result = await runTargetAsync(appData, 'deploy') + // expectStrings(result.stdout, [ + // `Running target deploy for project ${appData.projectName}`, + // `nx run ${appData.projectName}:deploy`, + // `nx run ${appData.projectName}:firebase deploy`, + // ]) + // // build target will also execute, since functions are implicit dep of app + // expectStrings(result.stdout, [ + // `nx run ${appData.projectName}:build`, + // `nx run ${functionData.projectName}:build`, + // `nx run ${functionData2.projectName}:build`, + // `Build succeeded`, + // ]) + // expectStrings(result.stderr, [ + // `${functionData.distDir}/main.js`, + // `${functionData2.distDir}/main.js`, + // ]) + // // cleanup + // await cleanFunctionAsync(functionData2) + // await cleanFunctionAsync(functionData) + // await cleanAppAsync(appData) + // }) - //-------------------------------------------------------------------------------------------------- - // Test migrations - //-------------------------------------------------------------------------------------------------- - // describe('nx-firebase sync migrate', () => { // it( - // 'should successfuly sync migrate for legacy app', + // 'should run deploy target for function', // async () => { - // const appData = getProjectData('apps', uniq('firebaseMigrateApp')) - // const functionData = getProjectData('apps', uniq('firebaseMigrateFunction')) + // const appData = getProjectData('apps', uniq('firebaseTargetsApp')) + // const functionData = getProjectData('apps', uniq('firebaseTargetsFunction')) // await appGeneratorAsync(appData) // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - - - - // const result = await syncGeneratorAsync(`--migrate`) - // testDebug(result.stdout) + // const result = await runTargetAsync(functionData, 'deploy') // expectStrings(result.stdout, [ - // `CHANGE updating firebase target --project for '${appData.projectName}' to '--project=test2'`, - // `UPDATE apps/${appData.projectName}/project.json`, + // `Running target deploy for project ${functionData.projectName}`, + // `nx run ${appData.projectName}:deploy`, + // `nx run ${appData.projectName}:firebase deploy`, + // ]) + // // build target will also execute, since functions are implicit dep of app + // expectStrings(result.stdout, [ + // `nx run ${functionData.projectName}:build`, + // `Build succeeded`, + // ]) + // expectStrings(result.stderr, [ + // `${functionData.distDir}/main.js`, // ]) - // expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).toContain( - // `--project=test2` - // ) - // // cleanup // await cleanFunctionAsync(functionData) // await cleanAppAsync(appData) - // }) - // }) + // }) + // }) + + + //-------------------------------------------------------------------------------------------------- + // Test migrations + //-------------------------------------------------------------------------------------------------- + + describe('nx-firebase sync migrate', () => { + it( + 'should successfuly sync migrate for legacy app', + async () => { + const appData = getProjectData('apps', uniq('firebaseMigrateApp')) + const functionData = getProjectData('apps', uniq('firebaseMigrateFunction')) + await appGeneratorAsync(appData) + await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + + const result = await syncGeneratorAsync(`--migrate`) + testDebug(result.stdout) + expectStrings(result.stdout, [ + `Running plugin migrations for workspace`, + ]) + + // modify firebase app to be v2 schema + const projectFile = `${appData.projectDir}/project.json` + const projectJson = readJson(projectFile) + projectJson.targets["serve"].executor = "nx:run-commands" + projectJson.targets["getconfig"].options.command = `nx run ${appData.projectName}:firebase functions:config:get > ${appData.projectDir}/.runtimeconfig.json` + updateFile(projectFile, JSON.stringify(projectJson, null, 3)) + + // remove environment folder from app + // const envDir = + // rmSync(envDir, { recursive: true, force: true }); + // cant delete in e2e, so lets just rename environment dir for now + renameFile(joinPathFragments(appData.projectDir, 'environment'), joinPathFragments(appData.projectDir, 'environment_old')) + + // modify firebase.json to be v2 schema + // const configFile = `firebase.json` + // const configJson = readJson(configFile) + // configJson.functions + + // remove globs from function project + + // expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).toContain( + // `--project=test2` + // ) + + // // check that app project has correct --config setting after rename + // expect(readJson(`${renamedAppData.projectDir}/project.json`).targets.firebase.options.command).toContain( + // `--config=${renamedAppData.configName}` + // ) + + + // re-run migrate script + const result2 = await syncGeneratorAsync(`--migrate`) + testDebug(result2.stdout) + expectStrings(result2.stdout, [ + `Running plugin migrations for workspace`, + `MIGRATE Added default environment file 'environment/.env' for firebase app '${appData.projectName}'`, + `MIGRATE Added default environment file 'environment/.env.local' for firebase app '${appData.projectName}'`, + `MIGRATE Added default environment file 'environment/.secret.local' for firebase app '${appData.projectName}'`, + `MIGRATE Updated getconfig target to use ignore environment directory for firebase app '${appData.projectName}'`, + `MIGRATE Updated serve target for firebase app '${appData.projectName}'`, + `UPDATE firebase.json`, + `CREATE ${appData.projectDir}/environment/.env`, + `CREATE ${appData.projectDir}/environment/.env.local`, + `CREATE ${appData.projectDir}/environment/.secret.local`, + `UPDATE ${appData.projectDir}/project.json`, + ]) + + validateProjectConfig(appData.projectDir, appData.projectName) +//todo: validateFunctionConfig + // cleanup + await cleanFunctionAsync(functionData) + await cleanAppAsync(appData) + }) + }) }) diff --git a/packages/nx-firebase/src/generators/sync/lib/migrate.ts b/packages/nx-firebase/src/generators/sync/lib/migrate.ts index 3e018094..de3b8aa4 100644 --- a/packages/nx-firebase/src/generators/sync/lib/migrate.ts +++ b/packages/nx-firebase/src/generators/sync/lib/migrate.ts @@ -16,7 +16,7 @@ export function runMigrations(tree: Tree, workspace: FirebaseWorkspace) { // ensure ignores in .nxignore // init generator takes care of this - // [2.0.0 -> 2.1.0] ensure environment files are present in apps + // [2.0.0 -> 2.1.0] ensure environment files are present in apps dir workspace.firebaseAppProjects.forEach((project, name) => { const envPath = `${project.root}/environment` const envFiles = [`.env`, `.env.local`, `.secret.local`] @@ -35,6 +35,9 @@ export function runMigrations(tree: Tree, workspace: FirebaseWorkspace) { ), ) tree.write(joinPathFragments(project.root, 'environment', envFile), src) + logger.info( + `MIGRATE Added default environment file 'environment/${envFile}' for firebase app '${name}'`, + ) } } }) @@ -43,11 +46,16 @@ export function runMigrations(tree: Tree, workspace: FirebaseWorkspace) { workspace.firebaseAppProjects.forEach((project, name) => { const getconfig = project.targets['getconfig'] const command = getconfig?.options.command as string - if (command) { + const legacyPath = joinPathFragments(project.root, '.runtimeconfig.json') + if (command.includes(legacyPath)) { getconfig.options.command = command.replace( - joinPathFragments(project.root, '.runtimeconfig.json'), + legacyPath, joinPathFragments(project.root, 'environment', '.runtimeconfig.json'), ) + logger.info( + `MIGRATE Updated getconfig target to use ignore environment directory for firebase app '${name}'`, + ) + updateProjectConfiguration(tree, project.name, project) } }) @@ -58,6 +66,9 @@ export function runMigrations(tree: Tree, workspace: FirebaseWorkspace) { const globs = `{ "glob": "**/*", "input": "${project.root}/environment", "output": "."}` if (!assets.includes(globs)) { assets.push(globs) + logger.info( + `MIGRATE Added assets glob for firebase function app '${name}'`, + ) updateProjectConfiguration(tree, project.name, project) } }) @@ -70,10 +81,14 @@ export function runMigrations(tree: Tree, workspace: FirebaseWorkspace) { } config.functions.map((func: FirebaseFunction) => { const ignoreRule = '*.local' - const ignore = func.ignore || [ignoreRule] - if (!ignore.includes[ignoreRule]) { + const ignore = func.ignore || [] + if (!ignore.includes(ignoreRule)) { + logger.info( + `MIGRATE Added ignore rule to firebase config '${configFilename}' for firebase function codebase '${func.codebase}'`, + ) ignore.push(ignoreRule) } + func.ignore = ignore }) writeJson(tree, configFilename, config) }) @@ -84,6 +99,8 @@ export function runMigrations(tree: Tree, workspace: FirebaseWorkspace) { const serveExecutor = '@simondotm/nx-firebase:serve' if (serve.executor !== serveExecutor) { serve.executor = serveExecutor + logger.info(`MIGRATE Updated serve target for firebase app '${name}'`) + updateProjectConfiguration(tree, project.name, project) } }) From b85a7c483105c52fa56cb19d55f17df9b50d76c7 Mon Sep 17 00:00:00 2001 From: Simon M Date: Wed, 20 Sep 2023 23:29:34 +0100 Subject: [PATCH 3/8] e2e fix --- e2e/nx-firebase-e2e/test-utils/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/nx-firebase-e2e/test-utils/index.ts b/e2e/nx-firebase-e2e/test-utils/index.ts index b28c4ab8..cb7775cc 100644 --- a/e2e/nx-firebase-e2e/test-utils/index.ts +++ b/e2e/nx-firebase-e2e/test-utils/index.ts @@ -266,7 +266,7 @@ export function expectedAppProjectTargets(projectDir: string, projectName: strin }, }, serve: { - executor: 'nx:run-commands', + executor: '@simondotm/nx-firebase:serve', options: { commands: [ `nx run ${projectName}:watch`, From 1733ed3f3911b51214bd2d921863314dedba676e Mon Sep 17 00:00:00 2001 From: Simon M Date: Wed, 20 Sep 2023 23:41:57 +0100 Subject: [PATCH 4/8] e2e fix --- e2e/nx-firebase-e2e/tests/nx-firebase.spec.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/e2e/nx-firebase-e2e/tests/nx-firebase.spec.ts b/e2e/nx-firebase-e2e/tests/nx-firebase.spec.ts index db4ac502..de2d6033 100644 --- a/e2e/nx-firebase-e2e/tests/nx-firebase.spec.ts +++ b/e2e/nx-firebase-e2e/tests/nx-firebase.spec.ts @@ -1131,9 +1131,11 @@ describe('nx-firebase e2e', () => { renameFile(joinPathFragments(appData.projectDir, 'environment'), joinPathFragments(appData.projectDir, 'environment_old')) // modify firebase.json to be v2 schema - // const configFile = `firebase.json` - // const configJson = readJson(configFile) - // configJson.functions + const configFile = `firebase.json` + const configJson = readJson(configFile) + delete configJson.functions[0].ignore + updateFile(configFile, JSON.stringify(configJson, null, 3)) + // remove globs from function project From 98a6fc61e83ce68b1a99b67e1f8e27147f5107f9 Mon Sep 17 00:00:00 2001 From: Simon M Date: Thu, 21 Sep 2023 02:07:48 +0100 Subject: [PATCH 5/8] e2e done --- e2e/nx-firebase-e2e/test-utils/index.ts | 6 + e2e/nx-firebase-e2e/tests/nx-firebase.spec.ts | 1828 +++++++++-------- 2 files changed, 928 insertions(+), 906 deletions(-) diff --git a/e2e/nx-firebase-e2e/test-utils/index.ts b/e2e/nx-firebase-e2e/test-utils/index.ts index cb7775cc..06234a8f 100644 --- a/e2e/nx-firebase-e2e/test-utils/index.ts +++ b/e2e/nx-firebase-e2e/test-utils/index.ts @@ -133,6 +133,12 @@ export function expectStrings(input: string, contains: string[]) { }) } +export function expectNoStrings(input: string, contains: string[]) { + contains.forEach((item) => { + expect(input).not.toContain(item) + }) +} + /** * Generate test project data * Note: call this function AFTER initial app firebase.json has been created in order to have a diff --git a/e2e/nx-firebase-e2e/tests/nx-firebase.spec.ts b/e2e/nx-firebase-e2e/tests/nx-firebase.spec.ts index de2d6033..e887bd94 100644 --- a/e2e/nx-firebase-e2e/tests/nx-firebase.spec.ts +++ b/e2e/nx-firebase-e2e/tests/nx-firebase.spec.ts @@ -1,14 +1,13 @@ import { - checkFilesExist, ensureNxProject, readJson, runNxCommandAsync, uniq, updateFile, + renameFile, + checkFilesExist, exists, readFile, - getCwd, - renameFile, } from '@nx/plugin/testing' import { @@ -20,19 +19,19 @@ import { expectStrings, functionGeneratorAsync, getProjectData, + syncGeneratorAsync, + safeRunNxCommandAsync, + validateProjectConfig, + expectNoStrings, libGeneratorAsync, - removeProjectAsync, - renameProjectAsync, runTargetAsync, - syncGeneratorAsync, getMainTs, getLibImport, addImport, - safeRunNxCommandAsync, - validateProjectConfig, + removeProjectAsync, + renameProjectAsync, } from '../test-utils' -import { ProjectConfiguration, joinPathFragments, writeJsonFile } from '@nx/devkit' -import { rmSync } from 'fs' +import { ProjectConfiguration, joinPathFragments } from '@nx/devkit' const JEST_TIMEOUT = 190000 @@ -169,933 +168,933 @@ describe('nx-firebase e2e', () => { }) - // //-------------------------------------------------------------------------------------------------- - // // Create Libraries for e2e function generator tests - // //-------------------------------------------------------------------------------------------------- - // describe('setup libraries', () => { - // it( - // 'should create buildable typescript library', - // async () => { - // await libGeneratorAsync(buildableLibData, `--buildable --importPath="${buildableLibData.npmScope}"`) - - // // no need to test the js library generator, only that it ran ok - // expect(() => - // checkFilesExist(`${buildableLibData.projectDir}/package.json`), - // ).not.toThrow() - - // const result = await runNxCommandAsync( - // `build ${buildableLibData.projectName}`, - // ) - // expect(result.stdout).toContain(compileComplete) - // expect(result.stdout).toContain( - // `${buildSuccess} ${buildableLibData.projectName}`, - // ) - // }) - - // it( - // 'should create buildable typescript library in subdir', - // async () => { - // await libGeneratorAsync(subDirBuildableLibData, `--directory=${subDirBuildableLibData.dir} --buildable --importPath="${subDirBuildableLibData.npmScope}"`) - - // // no need to test the js library generator, only that it ran ok - // expect(() => - // checkFilesExist(`${subDirBuildableLibData.projectDir}/package.json`), - // ).not.toThrow() - - // const result = await runNxCommandAsync( - // `build ${subDirBuildableLibData.projectName}`, - // ) - // expect(result.stdout).toContain(compileComplete) - // expect(result.stdout).toContain( - // `${buildSuccess} ${subDirBuildableLibData.projectName}`, - // ) - // }) - - // it( - // 'should create non-buildable typescript library', - // async () => { - // await libGeneratorAsync(nonbuildableLibData, `--buildable=false --importPath="${nonbuildableLibData.npmScope}"`) - - // expect(() => - // checkFilesExist(`${nonbuildableLibData.projectDir}/package.json`), - // ).toThrow() - - // const project = readJson( - // `${nonbuildableLibData.projectDir}/project.json`, - // ) - // expect(project.targets.build).not.toBeDefined() - // }) - - // it( - // 'should create non-buildable typescript library in subdir', - // async () => { - // // const projectData = getProjectData('libs', 'nonbuildablelib', { dir: 'subdir' }) - // await libGeneratorAsync(subDirNonbuildableLibData, `--directory=${subDirNonbuildableLibData.dir} --buildable=false --importPath="${subDirNonbuildableLibData.npmScope}"`) - - // expect(() => - // checkFilesExist(`${subDirNonbuildableLibData.projectDir}/package.json`), - // ).toThrow() - - // const project = readJson( - // `${subDirNonbuildableLibData.projectDir}/project.json`, - // ) - // expect(project.targets.build).not.toBeDefined() - // }) - // }) + //-------------------------------------------------------------------------------------------------- + // Create Libraries for e2e function generator tests + //-------------------------------------------------------------------------------------------------- + describe('setup libraries', () => { + it( + 'should create buildable typescript library', + async () => { + await libGeneratorAsync(buildableLibData, `--buildable --importPath="${buildableLibData.npmScope}"`) + + // no need to test the js library generator, only that it ran ok + expect(() => + checkFilesExist(`${buildableLibData.projectDir}/package.json`), + ).not.toThrow() + + const result = await runNxCommandAsync( + `build ${buildableLibData.projectName}`, + ) + expect(result.stdout).toContain(compileComplete) + expect(result.stdout).toContain( + `${buildSuccess} ${buildableLibData.projectName}`, + ) + }) + + it( + 'should create buildable typescript library in subdir', + async () => { + await libGeneratorAsync(subDirBuildableLibData, `--directory=${subDirBuildableLibData.dir} --buildable --importPath="${subDirBuildableLibData.npmScope}"`) + + // no need to test the js library generator, only that it ran ok + expect(() => + checkFilesExist(`${subDirBuildableLibData.projectDir}/package.json`), + ).not.toThrow() + + const result = await runNxCommandAsync( + `build ${subDirBuildableLibData.projectName}`, + ) + expect(result.stdout).toContain(compileComplete) + expect(result.stdout).toContain( + `${buildSuccess} ${subDirBuildableLibData.projectName}`, + ) + }) + + it( + 'should create non-buildable typescript library', + async () => { + await libGeneratorAsync(nonbuildableLibData, `--buildable=false --importPath="${nonbuildableLibData.npmScope}"`) + + expect(() => + checkFilesExist(`${nonbuildableLibData.projectDir}/package.json`), + ).toThrow() + + const project = readJson( + `${nonbuildableLibData.projectDir}/project.json`, + ) + expect(project.targets.build).not.toBeDefined() + }) + + it( + 'should create non-buildable typescript library in subdir', + async () => { + // const projectData = getProjectData('libs', 'nonbuildablelib', { dir: 'subdir' }) + await libGeneratorAsync(subDirNonbuildableLibData, `--directory=${subDirNonbuildableLibData.dir} --buildable=false --importPath="${subDirNonbuildableLibData.npmScope}"`) + + expect(() => + checkFilesExist(`${subDirNonbuildableLibData.projectDir}/package.json`), + ).toThrow() + + const project = readJson( + `${subDirNonbuildableLibData.projectDir}/project.json`, + ) + expect(project.targets.build).not.toBeDefined() + }) + }) - // //-------------------------------------------------------------------------------------------------- - // // Application generator e2e tests - // //-------------------------------------------------------------------------------------------------- - - // describe('nx-firebase application', () => { - - // it( - // 'should create nx-firebase app', - // async () => { - // const appData = getProjectData('apps', uniq('firebaseSetupApp')) - // await appGeneratorAsync(appData) - // // test generator output - // expect(() => - // checkFilesExist( - // ...expectedAppFiles(appData), - // ), - // ).not.toThrow() + //-------------------------------------------------------------------------------------------------- + // Application generator e2e tests + //-------------------------------------------------------------------------------------------------- + + describe('nx-firebase application', () => { + + it( + 'should create nx-firebase app', + async () => { + const appData = getProjectData('apps', uniq('firebaseSetupApp')) + await appGeneratorAsync(appData) + // test generator output + expect(() => + checkFilesExist( + ...expectedAppFiles(appData), + ), + ).not.toThrow() - // validateProjectConfig(appData.projectDir, appData.projectName) - - // // cleanup - app - // await cleanAppAsync(appData) - // }) - - // it( - // 'should build nx-firebase app', - // async () => { - // const appData = getProjectData('apps', uniq('firebaseSetupApp')) - // await appGeneratorAsync(appData) - - // // test app builder - // // at this point there are no functions so it doe nothing - // const result = await runNxCommandAsync(`build ${appData.projectName}`) - // expect(result.stdout).toContain("Build succeeded.") - - // // cleanup - app - // await cleanAppAsync(appData) - // }) - - // describe('--directory', () => { - // it( - // 'should create nx-firebase app in the specified directory', - // async () => { - // const appData = getProjectData('apps', uniq('firebaseSetupApp'), { dir: 'subdir' }) - // await appGeneratorAsync(appData, - // `--directory ${appData.dir}`, - // ) - // expect(() => - // checkFilesExist( - // ...expectedAppFiles(appData), - // ), - // ).not.toThrow() - - // const project = readJson(`${appData.projectDir}/project.json`) - // expect(project.name).toEqual(`${appData.projectName}`) - - // validateProjectConfig(appData.projectDir, appData.projectName) - - // // cleanup - app - // await cleanAppAsync(appData) - // }) - // }) - - // describe('--tags', () => { - // it( - // 'should add tags to the project', - // async () => { - // const appData = getProjectData('apps', uniq('firebaseSetupApp')) - // await appGeneratorAsync(appData, - // `--tags e2etag,e2ePackage`, - // ) - // const project = readJson(`${appData.projectDir}/project.json`) - // expect(project.tags).toEqual(['firebase:app', `firebase:name:${appData.projectName}`, 'e2etag', 'e2ePackage']) - - // // cleanup - app - // await cleanAppAsync(appData) - // } + validateProjectConfig(appData.projectDir, appData.projectName) + + // cleanup - app + await cleanAppAsync(appData) + }) + + it( + 'should build nx-firebase app', + async () => { + const appData = getProjectData('apps', uniq('firebaseSetupApp')) + await appGeneratorAsync(appData) + + // test app builder + // at this point there are no functions so it doe nothing + const result = await runNxCommandAsync(`build ${appData.projectName}`) + expect(result.stdout).toContain("Build succeeded.") + + // cleanup - app + await cleanAppAsync(appData) + }) + + describe('--directory', () => { + it( + 'should create nx-firebase app in the specified directory', + async () => { + const appData = getProjectData('apps', uniq('firebaseSetupApp'), { dir: 'subdir' }) + await appGeneratorAsync(appData, + `--directory ${appData.dir}`, + ) + expect(() => + checkFilesExist( + ...expectedAppFiles(appData), + ), + ).not.toThrow() + + const project = readJson(`${appData.projectDir}/project.json`) + expect(project.name).toEqual(`${appData.projectName}`) + + validateProjectConfig(appData.projectDir, appData.projectName) + + // cleanup - app + await cleanAppAsync(appData) + }) + }) + + describe('--tags', () => { + it( + 'should add tags to the project', + async () => { + const appData = getProjectData('apps', uniq('firebaseSetupApp')) + await appGeneratorAsync(appData, + `--tags e2etag,e2ePackage`, + ) + const project = readJson(`${appData.projectDir}/project.json`) + expect(project.tags).toEqual(['firebase:app', `firebase:name:${appData.projectName}`, 'e2etag', 'e2ePackage']) + + // cleanup - app + await cleanAppAsync(appData) + } - // ) - // }) - // }) + ) + }) + }) - // //-------------------------------------------------------------------------------------------------- - // // Function generator e2e tests - // //-------------------------------------------------------------------------------------------------- - - // describe('nx-firebase function', () => { - // it( - // 'should not create nx-firebase function without --app', - // async () => { - // const functionData = getProjectData('apps', uniq('firebaseFunction')) - // const result = await functionGeneratorAsync(functionData) - // expect(result.stdout).toContain("Required property 'app' is missing") - // // no cleanup required - // }) - - // it( - // 'should not create nx-firebase function with an invalid --app', - // async () => { - // const functionData = getProjectData('apps', uniq('firebaseFunction')) - // const result = await functionGeneratorAsync(functionData, '--app badapple') - // expect(result.stdout).toContain("A firebase application project called 'badapple' was not found in this workspace.") - // // no cleanup required - // }) - - // it( - // 'should create nx-firebase function', - // async () => { - // const appData = getProjectData('apps', uniq('firebaseApp')) - // const functionData = getProjectData('apps', uniq('firebaseFunction')) - // await appGeneratorAsync(appData) - // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - // // test generator output - // expect(() => - // checkFilesExist( - // ...expectedFunctionFiles(functionData) - // ), - // ).not.toThrow() - - // // check dist files dont exist and we havent accidentally run this test out of sequence - // expect(() => - // checkFilesExist( - // `dist/${functionData.projectDir}/main.js`, - // `dist/${functionData.projectDir}/package.json`, - // `dist/${functionData.projectDir}/.env`, - // `dist/${functionData.projectDir}/.env.local`, - // `dist/${functionData.projectDir}/.secret.local`, - // ), - // ).toThrow() + //-------------------------------------------------------------------------------------------------- + // Function generator e2e tests + //-------------------------------------------------------------------------------------------------- + + describe('nx-firebase function', () => { + it( + 'should not create nx-firebase function without --app', + async () => { + const functionData = getProjectData('apps', uniq('firebaseFunction')) + const result = await functionGeneratorAsync(functionData) + expect(result.stdout).toContain("Required property 'app' is missing") + // no cleanup required + }) + + it( + 'should not create nx-firebase function with an invalid --app', + async () => { + const functionData = getProjectData('apps', uniq('firebaseFunction')) + const result = await functionGeneratorAsync(functionData, '--app badapple') + expect(result.stdout).toContain("A firebase application project called 'badapple' was not found in this workspace.") + // no cleanup required + }) + + it( + 'should create nx-firebase function', + async () => { + const appData = getProjectData('apps', uniq('firebaseApp')) + const functionData = getProjectData('apps', uniq('firebaseFunction')) + await appGeneratorAsync(appData) + await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + // test generator output + expect(() => + checkFilesExist( + ...expectedFunctionFiles(functionData) + ), + ).not.toThrow() + + // check dist files dont exist and we havent accidentally run this test out of sequence + expect(() => + checkFilesExist( + `dist/${functionData.projectDir}/main.js`, + `dist/${functionData.projectDir}/package.json`, + `dist/${functionData.projectDir}/.env`, + `dist/${functionData.projectDir}/.env.local`, + `dist/${functionData.projectDir}/.secret.local`, + ), + ).toThrow() - // // cleanup - // await cleanFunctionAsync(functionData) - // await cleanAppAsync(appData) - // }) - - // it( - // 'should build nx-firebase function from the app', - // async () => { - // const appData = getProjectData('apps', uniq('firebaseApp')) - // const functionData = getProjectData('apps', uniq('firebaseFunction')) - // await appGeneratorAsync(appData) - // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - - // const result = await runTargetAsync(appData, 'build') - // expect(result.stdout).toContain("Build succeeded.") - - // expect(() => - // checkFilesExist( - // `dist/${functionData.projectDir}/main.js`, - // `dist/${functionData.projectDir}/package.json`, - // `dist/${functionData.projectDir}/.env`, - // `dist/${functionData.projectDir}/.env.local`, - // `dist/${functionData.projectDir}/.secret.local`, - // ), - // ).not.toThrow() + // cleanup + await cleanFunctionAsync(functionData) + await cleanAppAsync(appData) + }) + + it( + 'should build nx-firebase function from the app', + async () => { + const appData = getProjectData('apps', uniq('firebaseApp')) + const functionData = getProjectData('apps', uniq('firebaseFunction')) + await appGeneratorAsync(appData) + await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + + const result = await runTargetAsync(appData, 'build') + expect(result.stdout).toContain("Build succeeded.") + + expect(() => + checkFilesExist( + `dist/${functionData.projectDir}/main.js`, + `dist/${functionData.projectDir}/package.json`, + `dist/${functionData.projectDir}/.env`, + `dist/${functionData.projectDir}/.env.local`, + `dist/${functionData.projectDir}/.secret.local`, + ), + ).not.toThrow() - // // cleanup - // await cleanFunctionAsync(functionData) - // await cleanAppAsync(appData) - // }) - - // it( - // 'should build nx-firebase function directly', - // async () => { - // const appData = getProjectData('apps', uniq('firebaseApp')) - // const functionData = getProjectData('apps', uniq('firebaseFunction')) - // await appGeneratorAsync(appData) - // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - - // const result = await runTargetAsync(functionData, 'build') - // expect(result.stdout).toContain(`nx run ${functionData.projectName}:build`) - // // esbuild outputs to stderr for some reason - // expect(result.stderr).toContain(`${functionData.distDir}/main.js`) - // // make sure it hasnt bundled node_modules, indicator is that bundle size is megabytes in size - // expect(result.stderr).not.toContain(`Mb`) - - // // cleanup - // await cleanFunctionAsync(functionData) - // await cleanAppAsync(appData) - // }) - - - // it( - // 'should add correct dependencies to the built function package.json', - // async () => { - // const appData = getProjectData('apps', uniq('firebaseApp')) - // const functionData = getProjectData('apps', uniq('firebaseFunction')) - // await appGeneratorAsync(appData) - // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - - // const result = await runTargetAsync(functionData, 'build') - // expect(result.stdout).toContain( - // `Successfully ran target build for project ${functionData.projectName}`, - // ) - - // expectStrings(result.stderr, [ - // `${functionData.distDir}/main.js` - // ]) - // // make sure output build is not megabytes in size, which would mean we've - // // bundled node_modules as well - // expect(result.stdout).not.toContain('Mb') - - - // const distPackageFile = `${functionData.distDir}/package.json` - // expect(exists(distPackageFile)) - - // const distPackage = readJson(distPackageFile) - // const deps = distPackage['dependencies'] - // expect(deps).toBeDefined() - // // firebase-admin is No longer in the default main.ts template - // // expect(deps['firebase-admin']).toBeDefined() - // expect(deps['firebase-functions']).toBeDefined() - - // // cleanup - // await cleanFunctionAsync(functionData) - // await cleanAppAsync(appData) - // }) - - // it( - // 'should add tags to the function project', - // async () => { - // const appData = getProjectData('apps', uniq('firebaseApp')) - // const functionData = getProjectData('apps', uniq('firebaseFunction')) - // await appGeneratorAsync(appData) - // await functionGeneratorAsync(functionData, `--app ${appData.projectName} --tags e2etag,e2ePackage`) - - // const project = readJson(`${functionData.projectDir}/project.json`) - // expect(project.tags).toEqual([ - // 'firebase:function', - // `firebase:name:${functionData.projectName}`, - // `firebase:dep:${appData.projectName}`, - // 'e2etag', - // 'e2ePackage', - // ]) - - // // cleanup - // await cleanFunctionAsync(functionData) - // await cleanAppAsync(appData) - // } - // ) - // }) - - - // //-------------------------------------------------------------------------------------------------- - // // Test import & dependency handling - // //-------------------------------------------------------------------------------------------------- - - // describe('nx-firebase bundle dependencies', () => { - // it( - // 'should inline library dependencies into function bundle', - // async () => { - // // use libs we generated earler + // cleanup + await cleanFunctionAsync(functionData) + await cleanAppAsync(appData) + }) + + it( + 'should build nx-firebase function directly', + async () => { + const appData = getProjectData('apps', uniq('firebaseApp')) + const functionData = getProjectData('apps', uniq('firebaseFunction')) + await appGeneratorAsync(appData) + await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + + const result = await runTargetAsync(functionData, 'build') + expect(result.stdout).toContain(`nx run ${functionData.projectName}:build`) + // esbuild outputs to stderr for some reason + expect(result.stderr).toContain(`${functionData.distDir}/main.js`) + // make sure it hasnt bundled node_modules, indicator is that bundle size is megabytes in size + expect(result.stderr).not.toContain(`Mb`) + + // cleanup + await cleanFunctionAsync(functionData) + await cleanAppAsync(appData) + }) + + + it( + 'should add correct dependencies to the built function package.json', + async () => { + const appData = getProjectData('apps', uniq('firebaseApp')) + const functionData = getProjectData('apps', uniq('firebaseFunction')) + await appGeneratorAsync(appData) + await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + + const result = await runTargetAsync(functionData, 'build') + expect(result.stdout).toContain( + `Successfully ran target build for project ${functionData.projectName}`, + ) + + expectStrings(result.stderr, [ + `${functionData.distDir}/main.js` + ]) + // make sure output build is not megabytes in size, which would mean we've + // bundled node_modules as well + expect(result.stdout).not.toContain('Mb') + + + const distPackageFile = `${functionData.distDir}/package.json` + expect(exists(distPackageFile)) + + const distPackage = readJson(distPackageFile) + const deps = distPackage['dependencies'] + expect(deps).toBeDefined() + // firebase-admin is No longer in the default main.ts template + // expect(deps['firebase-admin']).toBeDefined() + expect(deps['firebase-functions']).toBeDefined() + + // cleanup + await cleanFunctionAsync(functionData) + await cleanAppAsync(appData) + }) + + it( + 'should add tags to the function project', + async () => { + const appData = getProjectData('apps', uniq('firebaseApp')) + const functionData = getProjectData('apps', uniq('firebaseFunction')) + await appGeneratorAsync(appData) + await functionGeneratorAsync(functionData, `--app ${appData.projectName} --tags e2etag,e2ePackage`) + + const project = readJson(`${functionData.projectDir}/project.json`) + expect(project.tags).toEqual([ + 'firebase:function', + `firebase:name:${functionData.projectName}`, + `firebase:dep:${appData.projectName}`, + 'e2etag', + 'e2ePackage', + ]) + + // cleanup + await cleanFunctionAsync(functionData) + await cleanAppAsync(appData) + } + ) + }) + + + //-------------------------------------------------------------------------------------------------- + // Test import & dependency handling + //-------------------------------------------------------------------------------------------------- + + describe('nx-firebase bundle dependencies', () => { + it( + 'should inline library dependencies into function bundle', + async () => { + // use libs we generated earler - // const appData = getProjectData('apps', uniq('firebaseDepsApp')) - // const functionData = getProjectData('apps', uniq('firebaseDepsFunction')) - // await appGeneratorAsync(appData) - // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + const appData = getProjectData('apps', uniq('firebaseDepsApp')) + const functionData = getProjectData('apps', uniq('firebaseDepsFunction')) + await appGeneratorAsync(appData) + await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - // // add buildable & nonbuildable lib dependencies using import statements - // let mainTs = getMainTs() + // add buildable & nonbuildable lib dependencies using import statements + let mainTs = getMainTs() - // // import from a buildable lib - // const libImport1 = getLibImport(buildableLibData) - // const importAddition1 = `import { ${libImport1} } from '${buildableLibData.npmScope}'\nconsole.log(${libImport1}())\n` - // mainTs = addImport(mainTs, importAddition1) + // import from a buildable lib + const libImport1 = getLibImport(buildableLibData) + const importAddition1 = `import { ${libImport1} } from '${buildableLibData.npmScope}'\nconsole.log(${libImport1}())\n` + mainTs = addImport(mainTs, importAddition1) - // // import from a non buildable lib - // const libImport2 = getLibImport(nonbuildableLibData) - // const importAddition2 = `import { ${libImport2} } from '${nonbuildableLibData.npmScope}'\nconsole.log(${libImport2}())\n` - // mainTs = addImport(mainTs, importAddition2) + // import from a non buildable lib + const libImport2 = getLibImport(nonbuildableLibData) + const importAddition2 = `import { ${libImport2} } from '${nonbuildableLibData.npmScope}'\nconsole.log(${libImport2}())\n` + mainTs = addImport(mainTs, importAddition2) - // // import from a buildable subdir lib - // const libImport3 = getLibImport(subDirBuildableLibData) - // const importAddition3 = `import { ${libImport3} } from '${subDirBuildableLibData.npmScope}'\nconsole.log(${libImport3}())\n` - // mainTs = addImport(mainTs, importAddition3) + // import from a buildable subdir lib + const libImport3 = getLibImport(subDirBuildableLibData) + const importAddition3 = `import { ${libImport3} } from '${subDirBuildableLibData.npmScope}'\nconsole.log(${libImport3}())\n` + mainTs = addImport(mainTs, importAddition3) - // // import from a non buildable subdir lib - // const libImport4 = getLibImport(subDirNonbuildableLibData) - // const importAddition4 = `import { ${libImport4} } from '${subDirNonbuildableLibData.npmScope}'\nconsole.log(${libImport4}())\n` - // mainTs = addImport(mainTs, importAddition4) - - // // write the new main.ts - // updateFile(functionData.mainTsPath, (content: string) => { - // return mainTs - // }) - - - // // confirm the file changes - // const updatedMainTs = readFile(functionData.mainTsPath) - // expect(updatedMainTs).toContain(importAddition1) - // expect(updatedMainTs).toContain(importAddition2) - // expect(updatedMainTs).toContain(importAddition3) - // expect(updatedMainTs).toContain(importAddition4) - - // // build - // const result = await runTargetAsync(functionData, `build`) - // // check console output - // expectStrings(result.stdout, [ - // `Running target build for project ${functionData.projectName}`, - // `nx run ${buildableLibData.projectName}:build`, - // `nx run ${subDirBuildableLibData.projectName}:build`, - // `Compiling TypeScript files for project "${subDirBuildableLibData.projectName}"`, - // `Compiling TypeScript files for project "${buildableLibData.projectName}"`, - // `Done compiling TypeScript files for project "${buildableLibData.projectName}"`, - // `Done compiling TypeScript files for project "${subDirBuildableLibData.projectName}"`, - // `nx run ${functionData.projectName}:build`, - // `Successfully ran target build for project ${functionData.projectName}`, - // ]) - // expectStrings(result.stderr, [ - // `${functionData.distDir}/main.js` - // ]) - // // make sure output build is not megabytes in size, which would mean we've - // // bundled node_modules as well - // expect(result.stdout).not.toContain('Mb') - - // // check dist outputs - // expect(() => - // checkFilesExist( - // `${functionData.distDir}/package.json`, - // `${functionData.distDir}/main.js`, - // `${functionData.distDir}/.env`, - // `${functionData.distDir}/.env.local`, - // `${functionData.distDir}/.secret.local`, - // ), - // ).not.toThrow() - - // // check dist package contains external imports - // const distPackage = readJson(`${functionData.distDir}/package.json`) - // const deps = distPackage['dependencies'] - // expect(deps).toBeDefined() - // // firebase-admin not in the template anymore - // // expect(deps['firebase-admin']).toBeDefined() - // expect(deps['firebase-functions']).toBeDefined() - - // // check bundled code contains the libcode we added - // const bundle = readFile(`${functionData.distDir}/main.js`) - - // // check that node modules were not bundled, happens in e2e if nx reset not called - // // probably the earlier check for deps in the package.json already detects this scenario too - // expect(bundle).not.toContain(`require_firebase_app`) - - // // our imported lib modules should be inlined in the bundle - // expect(bundle).toContain(`function ${libImport1}`) - // expect(bundle).toContain(`return "${buildableLibData.projectName}"`) - // expect(bundle).toContain(`function ${libImport2}`) - // expect(bundle).toContain(`return "${nonbuildableLibData.projectName}"`) - // expect(bundle).toContain(`function ${libImport3}`) - // expect(bundle).toContain(`return "${subDirBuildableLibData.projectName}"`) - // expect(bundle).toContain(`function ${libImport4}`) - // expect(bundle).toContain(`return "${subDirNonbuildableLibData.projectName}"`) - - // // cleanup - // await cleanFunctionAsync(functionData) - // await cleanAppAsync(appData) - // }) - - // }) - - - - // // test the nx-firebase sync generator - // describe('nx-firebase sync', () => { - - - // it( - // 'should sync firebase workspace with no changes', - // async () => { - // const result = await syncGeneratorAsync() - // testDebug(result.stdout) - // expect(result.stdout).not.toContain('CHANGE') - // expect(result.stdout).not.toContain('UPDATE') - // expect(result.stdout).not.toContain('CREATE') - // expect(result.stdout).not.toContain('DELETE') - // }) - - // describe('--project', () => { - // it( - // 'should set firebase app project using --project', - // async () => { - // // create firebase app without specifying firebase deploy --project - // const appData = getProjectData('apps', uniq('firebaseSyncApp')) - // await appGeneratorAsync(appData) - - // expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).not.toContain( - // `--project` - // ) - // const result = await syncGeneratorAsync(`--app=${appData.projectName} --project=test`) - // testDebug(result.stdout) - // expectStrings(result.stdout, [ - // `CHANGE setting firebase target --project for '${appData.projectName}' to '--project=test'`, - // `UPDATE apps/${appData.projectName}/project.json`, - // ]) - // expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).toContain( - // `--project=test` - // ) - // // cleanup - app - // await cleanAppAsync(appData) - // }) - - // it( - // 'should update firebase app project using --project', - // async () => { + // import from a non buildable subdir lib + const libImport4 = getLibImport(subDirNonbuildableLibData) + const importAddition4 = `import { ${libImport4} } from '${subDirNonbuildableLibData.npmScope}'\nconsole.log(${libImport4}())\n` + mainTs = addImport(mainTs, importAddition4) + + // write the new main.ts + updateFile(functionData.mainTsPath, (content: string) => { + return mainTs + }) + + + // confirm the file changes + const updatedMainTs = readFile(functionData.mainTsPath) + expect(updatedMainTs).toContain(importAddition1) + expect(updatedMainTs).toContain(importAddition2) + expect(updatedMainTs).toContain(importAddition3) + expect(updatedMainTs).toContain(importAddition4) + + // build + const result = await runTargetAsync(functionData, `build`) + // check console output + expectStrings(result.stdout, [ + `Running target build for project ${functionData.projectName}`, + `nx run ${buildableLibData.projectName}:build`, + `nx run ${subDirBuildableLibData.projectName}:build`, + `Compiling TypeScript files for project "${subDirBuildableLibData.projectName}"`, + `Compiling TypeScript files for project "${buildableLibData.projectName}"`, + `Done compiling TypeScript files for project "${buildableLibData.projectName}"`, + `Done compiling TypeScript files for project "${subDirBuildableLibData.projectName}"`, + `nx run ${functionData.projectName}:build`, + `Successfully ran target build for project ${functionData.projectName}`, + ]) + expectStrings(result.stderr, [ + `${functionData.distDir}/main.js` + ]) + // make sure output build is not megabytes in size, which would mean we've + // bundled node_modules as well + expect(result.stdout).not.toContain('Mb') + + // check dist outputs + expect(() => + checkFilesExist( + `${functionData.distDir}/package.json`, + `${functionData.distDir}/main.js`, + `${functionData.distDir}/.env`, + `${functionData.distDir}/.env.local`, + `${functionData.distDir}/.secret.local`, + ), + ).not.toThrow() + + // check dist package contains external imports + const distPackage = readJson(`${functionData.distDir}/package.json`) + const deps = distPackage['dependencies'] + expect(deps).toBeDefined() + // firebase-admin not in the template anymore + // expect(deps['firebase-admin']).toBeDefined() + expect(deps['firebase-functions']).toBeDefined() + + // check bundled code contains the libcode we added + const bundle = readFile(`${functionData.distDir}/main.js`) + + // check that node modules were not bundled, happens in e2e if nx reset not called + // probably the earlier check for deps in the package.json already detects this scenario too + expect(bundle).not.toContain(`require_firebase_app`) + + // our imported lib modules should be inlined in the bundle + expect(bundle).toContain(`function ${libImport1}`) + expect(bundle).toContain(`return "${buildableLibData.projectName}"`) + expect(bundle).toContain(`function ${libImport2}`) + expect(bundle).toContain(`return "${nonbuildableLibData.projectName}"`) + expect(bundle).toContain(`function ${libImport3}`) + expect(bundle).toContain(`return "${subDirBuildableLibData.projectName}"`) + expect(bundle).toContain(`function ${libImport4}`) + expect(bundle).toContain(`return "${subDirNonbuildableLibData.projectName}"`) + + // cleanup + await cleanFunctionAsync(functionData) + await cleanAppAsync(appData) + }) + + }) + + + + // test the nx-firebase sync generator + describe('nx-firebase sync', () => { + + + it( + 'should sync firebase workspace with no changes', + async () => { + const result = await syncGeneratorAsync() + testDebug(result.stdout) + expect(result.stdout).not.toContain('CHANGE') + expect(result.stdout).not.toContain('UPDATE') + expect(result.stdout).not.toContain('CREATE') + expect(result.stdout).not.toContain('DELETE') + }) + + describe('--project', () => { + it( + 'should set firebase app project using --project', + async () => { + // create firebase app without specifying firebase deploy --project + const appData = getProjectData('apps', uniq('firebaseSyncApp')) + await appGeneratorAsync(appData) + + expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).not.toContain( + `--project` + ) + const result = await syncGeneratorAsync(`--app=${appData.projectName} --project=test`) + testDebug(result.stdout) + expectStrings(result.stdout, [ + `CHANGE setting firebase target --project for '${appData.projectName}' to '--project=test'`, + `UPDATE apps/${appData.projectName}/project.json`, + ]) + expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).toContain( + `--project=test` + ) + // cleanup - app + await cleanAppAsync(appData) + }) + + it( + 'should update firebase app project using --project', + async () => { - // // create firebase app specifying firebase deploy --project - // const appData = getProjectData('apps', uniq('firebaseSyncApp')) - // await appGeneratorAsync(appData, `--project=test`) - - // expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).toContain( - // `--project=test` - // ) - // const result = await syncGeneratorAsync(`--app=${appData.projectName} --project=test2`) - // testDebug(result.stdout) - // expectStrings(result.stdout, [ - // `CHANGE updating firebase target --project for '${appData.projectName}' to '--project=test2'`, - // `UPDATE apps/${appData.projectName}/project.json`, - // ]) - // expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).toContain( - // `--project=test2` - // ) - - // // cleanup - app - // await cleanAppAsync(appData) - // }) - // }) - - // describe('deletions', () => { - - // it( - // 'should detect deleted firebase functions', - // async () => { - // const appData = getProjectData('apps', uniq('firebaseSyncApp')) - // const functionData = getProjectData('apps', uniq('firebaseSyncFunction')) - // await appGeneratorAsync(appData) - // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - - // await removeProjectAsync(functionData) - - // const result = await syncGeneratorAsync() - // testDebug(result.stdout) - // expectStrings(result.stdout, [ - // `CHANGE Firebase function '${functionData.projectName}' was deleted, removing function codebase from '${appData.configName}'`, - // `UPDATE ${appData.configName}`, - // ]) - - // // cleanup - app only, already removed function - // await cleanAppAsync(appData) - // }) - - // it( - // 'should detect deleted firebase apps', - // async () => { - // const appData = getProjectData('apps', uniq('firebaseSyncApp')) - // const functionData = getProjectData('apps', uniq('firebaseSyncFunction')) - // await appGeneratorAsync(appData) - // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - - // await removeProjectAsync(appData) - - // const result = await syncGeneratorAsync() - // testDebug(result.stdout) - // expectStrings(result.stdout, [ - // `DELETE ${appData.configName}`, - // ]) - // expectStrings(result.stderr, [ - // `CHANGE Firebase app '${appData.projectName}' was deleted, firebase:dep tag for firebase function '${functionData.projectName}' is no longer linked to a Firebase app.`, - // ]) + // create firebase app specifying firebase deploy --project + const appData = getProjectData('apps', uniq('firebaseSyncApp')) + await appGeneratorAsync(appData, `--project=test`) + + expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).toContain( + `--project=test` + ) + const result = await syncGeneratorAsync(`--app=${appData.projectName} --project=test2`) + testDebug(result.stdout) + expectStrings(result.stdout, [ + `CHANGE updating firebase target --project for '${appData.projectName}' to '--project=test2'`, + `UPDATE apps/${appData.projectName}/project.json`, + ]) + expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).toContain( + `--project=test2` + ) + + // cleanup - app + await cleanAppAsync(appData) + }) + }) + + describe('deletions', () => { + + it( + 'should detect deleted firebase functions', + async () => { + const appData = getProjectData('apps', uniq('firebaseSyncApp')) + const functionData = getProjectData('apps', uniq('firebaseSyncFunction')) + await appGeneratorAsync(appData) + await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + + await removeProjectAsync(functionData) + + const result = await syncGeneratorAsync() + testDebug(result.stdout) + expectStrings(result.stdout, [ + `CHANGE Firebase function '${functionData.projectName}' was deleted, removing function codebase from '${appData.configName}'`, + `UPDATE ${appData.configName}`, + ]) + + // cleanup - app only, already removed function + await cleanAppAsync(appData) + }) + + it( + 'should detect deleted firebase apps', + async () => { + const appData = getProjectData('apps', uniq('firebaseSyncApp')) + const functionData = getProjectData('apps', uniq('firebaseSyncFunction')) + await appGeneratorAsync(appData) + await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + + await removeProjectAsync(appData) + + const result = await syncGeneratorAsync() + testDebug(result.stdout) + expectStrings(result.stdout, [ + `DELETE ${appData.configName}`, + ]) + expectStrings(result.stderr, [ + `CHANGE Firebase app '${appData.projectName}' was deleted, firebase:dep tag for firebase function '${functionData.projectName}' is no longer linked to a Firebase app.`, + ]) - // // NOTE: - // // a deleted firebase app means the assets glob input dir in a function is no longer valid - // // this is ok though because Nx quietly fails when processing assets for an invalid input dir - - // // cleanup - function only, already removed app - // await cleanFunctionAsync(functionData) - // }) - - // it( - // 'should warn when no firebase apps use firebase.json config', - // async () => { - // const appData = getProjectData('apps', uniq('firebaseSyncApp')) - // const appData2 = getProjectData('apps', uniq('firebaseSyncApp'), {customConfig: true}) - // await appGeneratorAsync(appData) - // await appGeneratorAsync(appData2) - - // // delete the app that used firebase.json - // await removeProjectAsync(appData) - - // const result = await syncGeneratorAsync() - // testDebug(result.stdout) - // expectStrings(result.stderr, [ - // `None of the Firebase apps in this workspace use 'firebase.json' as their config. Firebase CLI may not work as expected. This can be fixed by renaming the config for one of your firebase projects to 'firebase.json'.`, - // ]) + // NOTE: + // a deleted firebase app means the assets glob input dir in a function is no longer valid + // this is ok though because Nx quietly fails when processing assets for an invalid input dir + + // cleanup - function only, already removed app + await cleanFunctionAsync(functionData) + }) + + it( + 'should warn when no firebase apps use firebase.json config', + async () => { + const appData = getProjectData('apps', uniq('firebaseSyncApp')) + const appData2 = getProjectData('apps', uniq('firebaseSyncApp'), {customConfig: true}) + await appGeneratorAsync(appData) + await appGeneratorAsync(appData2) + + // delete the app that used firebase.json + await removeProjectAsync(appData) + + const result = await syncGeneratorAsync() + testDebug(result.stdout) + expectStrings(result.stderr, [ + `None of the Firebase apps in this workspace use 'firebase.json' as their config. Firebase CLI may not work as expected. This can be fixed by renaming the config for one of your firebase projects to 'firebase.json'.`, + ]) - // // cleanup - second app - // await cleanFunctionAsync(appData2) - // }) + // cleanup - second app + await cleanFunctionAsync(appData2) + }) - // }) + }) - // describe('renames', () => { + describe('renames', () => { - // it( - // 'should detect renamed firebase functions', - // async () => { - // const appData = getProjectData('apps', uniq('firebaseSyncApp')) - // const functionData = getProjectData('apps', uniq('firebaseSyncFunction')) - // const renamedFunctionData = getProjectData('apps', uniq('firebaseSyncFunction')) - // await appGeneratorAsync(appData) - // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + it( + 'should detect renamed firebase functions', + async () => { + const appData = getProjectData('apps', uniq('firebaseSyncApp')) + const functionData = getProjectData('apps', uniq('firebaseSyncFunction')) + const renamedFunctionData = getProjectData('apps', uniq('firebaseSyncFunction')) + await appGeneratorAsync(appData) + await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - // expect(readJson(`${functionData.projectDir}/project.json`).targets.deploy.options.command).toContain( - // `--only functions:${functionData.projectName}` - // ) + expect(readJson(`${functionData.projectDir}/project.json`).targets.deploy.options.command).toContain( + `--only functions:${functionData.projectName}` + ) - // await renameProjectAsync(functionData, renamedFunctionData) + await renameProjectAsync(functionData, renamedFunctionData) - // const result = await syncGeneratorAsync() - // testDebug(result.stdout) + const result = await syncGeneratorAsync() + testDebug(result.stdout) - // expectStrings(result.stdout, [ - // `CHANGE Firebase function '${functionData.projectName}' was renamed to '${renamedFunctionData.projectName}', updated firebase:name tag`, - // `CHANGE Firebase function '${functionData.projectName}' was renamed to '${renamedFunctionData.projectName}', updated codebase in '${appData.configName}'`, - // `CHANGE Firebase function '${functionData.projectName}' was renamed to '${renamedFunctionData.projectName}', updated deploy target to '--only=functions:${renamedFunctionData.projectName}'`, - // `UPDATE apps/${renamedFunctionData.projectName}/project.json`, - // `UPDATE ${appData.configName}`, - // ]) + expectStrings(result.stdout, [ + `CHANGE Firebase function '${functionData.projectName}' was renamed to '${renamedFunctionData.projectName}', updated firebase:name tag`, + `CHANGE Firebase function '${functionData.projectName}' was renamed to '${renamedFunctionData.projectName}', updated codebase in '${appData.configName}'`, + `CHANGE Firebase function '${functionData.projectName}' was renamed to '${renamedFunctionData.projectName}', updated deploy target to '--only=functions:${renamedFunctionData.projectName}'`, + `UPDATE apps/${renamedFunctionData.projectName}/project.json`, + `UPDATE ${appData.configName}`, + ]) - // expect(readJson(`${renamedFunctionData.projectDir}/project.json`).targets.deploy.options.command).toContain( - // `--only functions:${renamedFunctionData.projectName}` - // ) + expect(readJson(`${renamedFunctionData.projectDir}/project.json`).targets.deploy.options.command).toContain( + `--only functions:${renamedFunctionData.projectName}` + ) - // // cleanup - function, then app - // await cleanFunctionAsync(renamedFunctionData) - // await cleanAppAsync(appData) - // }) - - - // it( - // 'should detect renamed firebase apps', - // async () => { - // const appData = getProjectData('apps', uniq('firebaseSyncApp')) - // // we will attach two functions to the app for this test - // const functionData = getProjectData('apps', uniq('firebaseSyncFunction')) - // const functionData2 = getProjectData('apps', uniq('firebaseSyncFunction')) - // const renamedAppData = getProjectData('apps', uniq('firebaseSyncApp')) - // await appGeneratorAsync(appData) - // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - // await functionGeneratorAsync(functionData2, `--app ${appData.projectName}`) - - // await renameProjectAsync(appData, renamedAppData) - - // const result = await syncGeneratorAsync() - // testDebug(result.stdout) - - // expectStrings(result.stdout, [ - // `CHANGE Firebase app '${appData.projectName}' linked to primary config file was renamed to '${renamedAppData.projectName}', skipping rename of '${renamedAppData.configName}'`, - // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:name tag`, - // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated targets`, - // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:dep tag in firebase function '${functionData.projectName}'`, - // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated environment assets path in firebase function '${functionData.projectName}'`, - // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:dep tag in firebase function '${functionData2.projectName}'`, - // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated environment assets path in firebase function '${functionData2.projectName}'`, - // `UPDATE apps/${renamedAppData.projectName}/project.json`, - // `UPDATE apps/${functionData.projectName}/project.json`, - // ]) - // // we should not rename config if it is called firebase.json - // expect(result.stdout).not.toContain( - // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', renamed config file to '${renamedAppData.configName}'`, - // ) - // expect(result.stdout).not.toContain( - // `DELETE ${appData.configName}`, - // ) - // expect(result.stdout).not.toContain( - // `CREATE ${renamedAppData.configName}`, - // ) - - // // check that app project has correct --config setting after rename - // expect(readJson(`${renamedAppData.projectDir}/project.json`).targets.firebase.options.command).toContain( - // `--config=${renamedAppData.configName}` - // ) + // cleanup - function, then app + await cleanFunctionAsync(renamedFunctionData) + await cleanAppAsync(appData) + }) + + + it( + 'should detect renamed firebase apps', + async () => { + const appData = getProjectData('apps', uniq('firebaseSyncApp')) + // we will attach two functions to the app for this test + const functionData = getProjectData('apps', uniq('firebaseSyncFunction')) + const functionData2 = getProjectData('apps', uniq('firebaseSyncFunction')) + const renamedAppData = getProjectData('apps', uniq('firebaseSyncApp')) + await appGeneratorAsync(appData) + await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + await functionGeneratorAsync(functionData2, `--app ${appData.projectName}`) + + await renameProjectAsync(appData, renamedAppData) + + const result = await syncGeneratorAsync() + testDebug(result.stdout) + + expectStrings(result.stdout, [ + `CHANGE Firebase app '${appData.projectName}' linked to primary config file was renamed to '${renamedAppData.projectName}', skipping rename of '${renamedAppData.configName}'`, + `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:name tag`, + `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated targets`, + `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:dep tag in firebase function '${functionData.projectName}'`, + `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated environment assets path in firebase function '${functionData.projectName}'`, + `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:dep tag in firebase function '${functionData2.projectName}'`, + `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated environment assets path in firebase function '${functionData2.projectName}'`, + `UPDATE apps/${renamedAppData.projectName}/project.json`, + `UPDATE apps/${functionData.projectName}/project.json`, + ]) + // we should not rename config if it is called firebase.json + expect(result.stdout).not.toContain( + `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', renamed config file to '${renamedAppData.configName}'`, + ) + expect(result.stdout).not.toContain( + `DELETE ${appData.configName}`, + ) + expect(result.stdout).not.toContain( + `CREATE ${renamedAppData.configName}`, + ) + + // check that app project has correct --config setting after rename + expect(readJson(`${renamedAppData.projectDir}/project.json`).targets.firebase.options.command).toContain( + `--config=${renamedAppData.configName}` + ) - // // check rename was successful - // validateProjectConfig(renamedAppData.projectDir, renamedAppData.projectName) + // check rename was successful + validateProjectConfig(renamedAppData.projectDir, renamedAppData.projectName) - // // run another sync to check there should be no orphaned functions from an app rename - // const result2 = await syncGeneratorAsync() - // expect(result2.stderr).not.toContain('is no longer linked to a Firebase app') - // expect(result2.stdout).not.toContain('UPDATE') - - // // cleanup - function, then app - // await cleanFunctionAsync(functionData) - // await cleanFunctionAsync(functionData2) - // await cleanAppAsync(renamedAppData) - // }) + // run another sync to check there should be no orphaned functions from an app rename + const result2 = await syncGeneratorAsync() + expect(result2.stderr).not.toContain('is no longer linked to a Firebase app') + expect(result2.stdout).not.toContain('UPDATE') + + // cleanup - function, then app + await cleanFunctionAsync(functionData) + await cleanFunctionAsync(functionData2) + await cleanAppAsync(renamedAppData) + }) - // it( - // 'should detect renamed firebase apps & functions', - // async () => { - // const appData = getProjectData('apps', uniq('firebaseSyncApp')) - // const functionData = getProjectData('apps', uniq('firebaseSyncFunction')) - // const renamedFunctionData = getProjectData('apps', uniq('firebaseSyncFunction')) - // const renamedAppData = getProjectData('apps', uniq('firebaseSyncApp')) - - // await appGeneratorAsync(appData) - // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - - // // rename app & function - // await renameProjectAsync(appData, renamedAppData) - // await renameProjectAsync(functionData, renamedFunctionData) - - // const result = await syncGeneratorAsync() - // testDebug(result.stdout) - - // expectStrings(result.stdout, [ - // `CHANGE Firebase app '${appData.projectName}' linked to primary config file was renamed to '${renamedAppData.projectName}', skipping rename of '${renamedAppData.configName}'`, - // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:name tag`, - // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated targets`, - // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:dep tag in firebase function '${renamedFunctionData.projectName}'`, - // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated environment assets path in firebase function '${renamedFunctionData.projectName}'`, - // `CHANGE Firebase function '${functionData.projectName}' was renamed to '${renamedFunctionData.projectName}', updated firebase:name tag`, - // `CHANGE Firebase function '${functionData.projectName}' was renamed to '${renamedFunctionData.projectName}', updated deploy target to '--only=functions:${renamedFunctionData.projectName}'`, - // `CHANGE Firebase function '${functionData.projectName}' was renamed to '${renamedFunctionData.projectName}', updated codebase in '${renamedAppData.configName}'`, - // `UPDATE apps/${renamedAppData.projectName}/project.json`, - // `UPDATE apps/${renamedFunctionData.projectName}/project.json`, - // ]) - // // we should not rename config if it is called firebase.json - // expect(result.stdout).not.toContain( - // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', renamed config file to '${renamedAppData.configName}'`, - // ) - // expect(result.stdout).not.toContain( - // `DELETE ${appData.configName}`, - // ) - // expect(result.stdout).not.toContain( - // `CREATE ${renamedAppData.configName}`, - // ) - - // // check that app project has correct --config setting after rename - // expect(readJson(`${renamedAppData.projectDir}/project.json`).targets.firebase.options.command).toContain( - // `--config=${renamedAppData.configName}` - // ) - // // check that function project has correct --config setting after rename - // expect(readJson(`${renamedFunctionData.projectDir}/project.json`).targets.deploy.options.command).toContain( - // `--only functions:${renamedFunctionData.projectName}` - // ) + it( + 'should detect renamed firebase apps & functions', + async () => { + const appData = getProjectData('apps', uniq('firebaseSyncApp')) + const functionData = getProjectData('apps', uniq('firebaseSyncFunction')) + const renamedFunctionData = getProjectData('apps', uniq('firebaseSyncFunction')) + const renamedAppData = getProjectData('apps', uniq('firebaseSyncApp')) + + await appGeneratorAsync(appData) + await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + + // rename app & function + await renameProjectAsync(appData, renamedAppData) + await renameProjectAsync(functionData, renamedFunctionData) + + const result = await syncGeneratorAsync() + testDebug(result.stdout) + + expectStrings(result.stdout, [ + `CHANGE Firebase app '${appData.projectName}' linked to primary config file was renamed to '${renamedAppData.projectName}', skipping rename of '${renamedAppData.configName}'`, + `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:name tag`, + `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated targets`, + `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:dep tag in firebase function '${renamedFunctionData.projectName}'`, + `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated environment assets path in firebase function '${renamedFunctionData.projectName}'`, + `CHANGE Firebase function '${functionData.projectName}' was renamed to '${renamedFunctionData.projectName}', updated firebase:name tag`, + `CHANGE Firebase function '${functionData.projectName}' was renamed to '${renamedFunctionData.projectName}', updated deploy target to '--only=functions:${renamedFunctionData.projectName}'`, + `CHANGE Firebase function '${functionData.projectName}' was renamed to '${renamedFunctionData.projectName}', updated codebase in '${renamedAppData.configName}'`, + `UPDATE apps/${renamedAppData.projectName}/project.json`, + `UPDATE apps/${renamedFunctionData.projectName}/project.json`, + ]) + // we should not rename config if it is called firebase.json + expect(result.stdout).not.toContain( + `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', renamed config file to '${renamedAppData.configName}'`, + ) + expect(result.stdout).not.toContain( + `DELETE ${appData.configName}`, + ) + expect(result.stdout).not.toContain( + `CREATE ${renamedAppData.configName}`, + ) + + // check that app project has correct --config setting after rename + expect(readJson(`${renamedAppData.projectDir}/project.json`).targets.firebase.options.command).toContain( + `--config=${renamedAppData.configName}` + ) + // check that function project has correct --config setting after rename + expect(readJson(`${renamedFunctionData.projectDir}/project.json`).targets.deploy.options.command).toContain( + `--only functions:${renamedFunctionData.projectName}` + ) - // // check rename was successful - // validateProjectConfig(renamedAppData.projectDir, renamedAppData.projectName) + // check rename was successful + validateProjectConfig(renamedAppData.projectDir, renamedAppData.projectName) - // // cleanup - function, then app - // await cleanFunctionAsync(renamedFunctionData) - // await cleanAppAsync(renamedAppData) - // }) - - - - // it( - // 'should rename configs for renamed firebase apps when multiple apps in workspace', - // async () => { - // expect(!exists('firebase.json')) - - // // create first project that will have the primary firebase.json config - // const appDataPrimary = getProjectData('apps', uniq('firebaseSyncApp')) - // const functionData = getProjectData('apps', uniq('firebaseSyncFunction')) - // await appGeneratorAsync(appDataPrimary) - - // expect(appDataPrimary.configName).toEqual('firebase.json') - // expect(readJson(`${appDataPrimary.projectDir}/project.json`).targets.firebase.options.command).toContain( - // `--config=firebase.json` - // ) - // expect(exists('firebase.json')) - - // // generate second app after first app is generated so that first config is detected - // const appData = getProjectData('apps', uniq('firebaseSyncApp'), {customConfig: true}) - // const renamedAppData = getProjectData('apps', uniq('firebaseSyncApp'), {customConfig: true}) - - // expect(appData.configName).not.toEqual('firebase.json') - // expect(renamedAppData.configName).not.toEqual('firebase.json') - - // await appGeneratorAsync(appData) - // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - - // expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).not.toContain( - // `--config=firebase.json` - // ) - // expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).toContain( - // `--config=${appData.configName}` - // ) - - // await renameProjectAsync(appData, renamedAppData) - - // const result = await syncGeneratorAsync() - // testDebug(result.stdout) - - // expectStrings(result.stdout, [ - // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', renamed config file to '${renamedAppData.configName}'`, - // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:name tag`, - // `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:dep tag in firebase function '${functionData.projectName}'`, - // `UPDATE apps/${renamedAppData.projectName}/project.json`, - // `UPDATE apps/${functionData.projectName}/project.json`, - // `DELETE ${appData.configName}`, - // `CREATE ${renamedAppData.configName}`, - // ]) - - // // check that app project has correct --config setting after rename - // expect(readJson(`${renamedAppData.projectDir}/project.json`).targets.firebase.options.command).toContain( - // `--config=${renamedAppData.configName}` - // ) + // cleanup - function, then app + await cleanFunctionAsync(renamedFunctionData) + await cleanAppAsync(renamedAppData) + }) + + + + it( + 'should rename configs for renamed firebase apps when multiple apps in workspace', + async () => { + expect(!exists('firebase.json')) + + // create first project that will have the primary firebase.json config + const appDataPrimary = getProjectData('apps', uniq('firebaseSyncApp')) + const functionData = getProjectData('apps', uniq('firebaseSyncFunction')) + await appGeneratorAsync(appDataPrimary) + + expect(appDataPrimary.configName).toEqual('firebase.json') + expect(readJson(`${appDataPrimary.projectDir}/project.json`).targets.firebase.options.command).toContain( + `--config=firebase.json` + ) + expect(exists('firebase.json')) + + // generate second app after first app is generated so that first config is detected + const appData = getProjectData('apps', uniq('firebaseSyncApp'), {customConfig: true}) + const renamedAppData = getProjectData('apps', uniq('firebaseSyncApp'), {customConfig: true}) + + expect(appData.configName).not.toEqual('firebase.json') + expect(renamedAppData.configName).not.toEqual('firebase.json') + + await appGeneratorAsync(appData) + await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + + expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).not.toContain( + `--config=firebase.json` + ) + expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).toContain( + `--config=${appData.configName}` + ) + + await renameProjectAsync(appData, renamedAppData) + + const result = await syncGeneratorAsync() + testDebug(result.stdout) + + expectStrings(result.stdout, [ + `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', renamed config file to '${renamedAppData.configName}'`, + `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:name tag`, + `CHANGE Firebase app '${appData.projectName}' was renamed to '${renamedAppData.projectName}', updated firebase:dep tag in firebase function '${functionData.projectName}'`, + `UPDATE apps/${renamedAppData.projectName}/project.json`, + `UPDATE apps/${functionData.projectName}/project.json`, + `DELETE ${appData.configName}`, + `CREATE ${renamedAppData.configName}`, + ]) + + // check that app project has correct --config setting after rename + expect(readJson(`${renamedAppData.projectDir}/project.json`).targets.firebase.options.command).toContain( + `--config=${renamedAppData.configName}` + ) - // // run another sync to check there should be no orphaned functions from an app rename - // const result2 = await syncGeneratorAsync() - // expect(result2.stderr).not.toContain('is no longer linked to a Firebase app') - // expect(result2.stdout).not.toContain('UPDATE') + // run another sync to check there should be no orphaned functions from an app rename + const result2 = await syncGeneratorAsync() + expect(result2.stderr).not.toContain('is no longer linked to a Firebase app') + expect(result2.stdout).not.toContain('UPDATE') - // // cleanup - function, then app - // await cleanFunctionAsync(functionData) - // await cleanAppAsync(renamedAppData, { appsRemaining: 1, functionsRemaining: 0 }) - // await cleanAppAsync(appDataPrimary) - // }) + // cleanup - function, then app + await cleanFunctionAsync(functionData) + await cleanAppAsync(renamedAppData, { appsRemaining: 1, functionsRemaining: 0 }) + await cleanAppAsync(appDataPrimary) + }) - // }) + }) - // }) + }) - // //-------------------------------------------------------------------------------------------------- - // // Test app targets - // //-------------------------------------------------------------------------------------------------- + //-------------------------------------------------------------------------------------------------- + // Test app targets + //-------------------------------------------------------------------------------------------------- - // describe('nx-firebase app targets', () => { - // it( - // 'should run lint target for app', - // async () => { - // const appData = getProjectData('apps', uniq('firebaseTargetsApp')) - // const functionData = getProjectData('apps', uniq('firebaseTargetsFunction')) - // const functionData2 = getProjectData('apps', uniq('firebaseTargetsFunction')) - // await appGeneratorAsync(appData) - // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - // await functionGeneratorAsync(functionData2, `--app ${appData.projectName}`) + describe('nx-firebase app targets', () => { + it( + 'should run lint target for app', + async () => { + const appData = getProjectData('apps', uniq('firebaseTargetsApp')) + const functionData = getProjectData('apps', uniq('firebaseTargetsFunction')) + const functionData2 = getProjectData('apps', uniq('firebaseTargetsFunction')) + await appGeneratorAsync(appData) + await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + await functionGeneratorAsync(functionData2, `--app ${appData.projectName}`) - // const result = await runTargetAsync(appData, 'lint') - // expectStrings(result.stdout, [ - // `nx run ${appData.projectName}:lint`, - // `Running target lint for 2 projects`, - // `nx run ${functionData.projectName}:lint`, - // `nx run ${functionData2.projectName}:lint`, - // `All files pass linting`, - // `Successfully ran target lint for 2 projects`, - // `Successfully ran target lint for project ${appData.projectName}`, - // ]) - - // // cleanup - // await cleanFunctionAsync(functionData2) - // await cleanFunctionAsync(functionData) - // await cleanAppAsync(appData) - // }) - - // it( - // 'should run test target for app', - // async () => { - // const appData = getProjectData('apps', uniq('firebaseTargetsApp')) - // const functionData = getProjectData('apps', uniq('firebaseTargetsFunction')) - // const functionData2 = getProjectData('apps', uniq('firebaseTargetsFunction')) - // await appGeneratorAsync(appData) - // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - // await functionGeneratorAsync(functionData2, `--app ${appData.projectName}`) + const result = await runTargetAsync(appData, 'lint') + expectStrings(result.stdout, [ + `nx run ${appData.projectName}:lint`, + `Running target lint for 2 projects`, + `nx run ${functionData.projectName}:lint`, + `nx run ${functionData2.projectName}:lint`, + `All files pass linting`, + `Successfully ran target lint for 2 projects`, + `Successfully ran target lint for project ${appData.projectName}`, + ]) + + // cleanup + await cleanFunctionAsync(functionData2) + await cleanFunctionAsync(functionData) + await cleanAppAsync(appData) + }) + + it( + 'should run test target for app', + async () => { + const appData = getProjectData('apps', uniq('firebaseTargetsApp')) + const functionData = getProjectData('apps', uniq('firebaseTargetsFunction')) + const functionData2 = getProjectData('apps', uniq('firebaseTargetsFunction')) + await appGeneratorAsync(appData) + await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + await functionGeneratorAsync(functionData2, `--app ${appData.projectName}`) - // const result = await runTargetAsync(appData, 'test') - // expectStrings(result.stdout, [ - // `nx run ${appData.projectName}:test`, - // `Running target test for 2 projects`, - // `nx run ${functionData.projectName}:test`, - // `nx run ${functionData2.projectName}:test`, - // `Successfully ran target test for 2 projects`, - // `Successfully ran target test for project ${appData.projectName}`, - // ]) - - // // cleanup - // await cleanFunctionAsync(functionData2) - // await cleanFunctionAsync(functionData) - // await cleanAppAsync(appData) - // }) - - - // it( - // 'should run deploy target for app', - // async () => { - // const appData = getProjectData('apps', uniq('firebaseTargetsApp')) - // const functionData = getProjectData('apps', uniq('firebaseDepsFunction')) - // const functionData2 = getProjectData('apps', uniq('firebaseDepsFunction')) - // await appGeneratorAsync(appData) - // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - // await functionGeneratorAsync(functionData2, `--app ${appData.projectName}`) + const result = await runTargetAsync(appData, 'test') + expectStrings(result.stdout, [ + `nx run ${appData.projectName}:test`, + `Running target test for 2 projects`, + `nx run ${functionData.projectName}:test`, + `nx run ${functionData2.projectName}:test`, + `Successfully ran target test for 2 projects`, + `Successfully ran target test for project ${appData.projectName}`, + ]) + + // cleanup + await cleanFunctionAsync(functionData2) + await cleanFunctionAsync(functionData) + await cleanAppAsync(appData) + }) + + + it( + 'should run deploy target for app', + async () => { + const appData = getProjectData('apps', uniq('firebaseTargetsApp')) + const functionData = getProjectData('apps', uniq('firebaseDepsFunction')) + const functionData2 = getProjectData('apps', uniq('firebaseDepsFunction')) + await appGeneratorAsync(appData) + await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + await functionGeneratorAsync(functionData2, `--app ${appData.projectName}`) - // // deploy target will fail because theres no firebase project but thats ok - // // we cannot e2e a real firebase project setup atm - // const result = await runTargetAsync(appData, 'deploy') - // expectStrings(result.stdout, [ - // `Running target deploy for project ${appData.projectName}`, - // `nx run ${appData.projectName}:deploy`, - // `nx run ${appData.projectName}:firebase deploy`, - // ]) - // // build target will also execute, since functions are implicit dep of app - // expectStrings(result.stdout, [ - // `nx run ${appData.projectName}:build`, - // `nx run ${functionData.projectName}:build`, - // `nx run ${functionData2.projectName}:build`, - // `Build succeeded`, - // ]) - // expectStrings(result.stderr, [ - // `${functionData.distDir}/main.js`, - // `${functionData2.distDir}/main.js`, - // ]) - // // cleanup - // await cleanFunctionAsync(functionData2) - // await cleanFunctionAsync(functionData) - // await cleanAppAsync(appData) - // }) - - - // it( - // 'should run deploy target for function', - // async () => { - // const appData = getProjectData('apps', uniq('firebaseTargetsApp')) - // const functionData = getProjectData('apps', uniq('firebaseTargetsFunction')) - // await appGeneratorAsync(appData) - // await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) + // deploy target will fail because theres no firebase project but thats ok + // we cannot e2e a real firebase project setup atm + const result = await runTargetAsync(appData, 'deploy') + expectStrings(result.stdout, [ + `Running target deploy for project ${appData.projectName}`, + `nx run ${appData.projectName}:deploy`, + `nx run ${appData.projectName}:firebase deploy`, + ]) + // build target will also execute, since functions are implicit dep of app + expectStrings(result.stdout, [ + `nx run ${appData.projectName}:build`, + `nx run ${functionData.projectName}:build`, + `nx run ${functionData2.projectName}:build`, + `Build succeeded`, + ]) + expectStrings(result.stderr, [ + `${functionData.distDir}/main.js`, + `${functionData2.distDir}/main.js`, + ]) + // cleanup + await cleanFunctionAsync(functionData2) + await cleanFunctionAsync(functionData) + await cleanAppAsync(appData) + }) + + + it( + 'should run deploy target for function', + async () => { + const appData = getProjectData('apps', uniq('firebaseTargetsApp')) + const functionData = getProjectData('apps', uniq('firebaseTargetsFunction')) + await appGeneratorAsync(appData) + await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - // const result = await runTargetAsync(functionData, 'deploy') - // expectStrings(result.stdout, [ - // `Running target deploy for project ${functionData.projectName}`, - // `nx run ${appData.projectName}:deploy`, - // `nx run ${appData.projectName}:firebase deploy`, - // ]) - // // build target will also execute, since functions are implicit dep of app - // expectStrings(result.stdout, [ - // `nx run ${functionData.projectName}:build`, - // `Build succeeded`, - // ]) - // expectStrings(result.stderr, [ - // `${functionData.distDir}/main.js`, - // ]) - - // // cleanup - // await cleanFunctionAsync(functionData) - // await cleanAppAsync(appData) - // }) - // }) + const result = await runTargetAsync(functionData, 'deploy') + expectStrings(result.stdout, [ + `Running target deploy for project ${functionData.projectName}`, + `nx run ${appData.projectName}:deploy`, + `nx run ${appData.projectName}:firebase deploy`, + ]) + // build target will also execute, since functions are implicit dep of app + expectStrings(result.stdout, [ + `nx run ${functionData.projectName}:build`, + `Build succeeded`, + ]) + expectStrings(result.stderr, [ + `${functionData.distDir}/main.js`, + ]) + + // cleanup + await cleanFunctionAsync(functionData) + await cleanAppAsync(appData) + }) + }) //-------------------------------------------------------------------------------------------------- @@ -1125,8 +1124,6 @@ describe('nx-firebase e2e', () => { updateFile(projectFile, JSON.stringify(projectJson, null, 3)) // remove environment folder from app - // const envDir = - // rmSync(envDir, { recursive: true, force: true }); // cant delete in e2e, so lets just rename environment dir for now renameFile(joinPathFragments(appData.projectDir, 'environment'), joinPathFragments(appData.projectDir, 'environment_old')) @@ -1138,27 +1135,23 @@ describe('nx-firebase e2e', () => { // remove globs from function project - - // expect(readJson(`${appData.projectDir}/project.json`).targets.firebase.options.command).toContain( - // `--project=test2` - // ) - - // // check that app project has correct --config setting after rename - // expect(readJson(`${renamedAppData.projectDir}/project.json`).targets.firebase.options.command).toContain( - // `--config=${renamedAppData.configName}` - // ) - - - // re-run migrate script + const functionFile = `${functionData.projectDir}/project.json` + const functionJson = readJson(functionFile) + const options = functionJson.targets["build"].options + const assets = options.assets as string[] + options.assets = [ assets.shift() ] + updateFile(functionFile, JSON.stringify(functionJson, null, 3)) + + // run migrate script const result2 = await syncGeneratorAsync(`--migrate`) testDebug(result2.stdout) expectStrings(result2.stdout, [ - `Running plugin migrations for workspace`, `MIGRATE Added default environment file 'environment/.env' for firebase app '${appData.projectName}'`, `MIGRATE Added default environment file 'environment/.env.local' for firebase app '${appData.projectName}'`, `MIGRATE Added default environment file 'environment/.secret.local' for firebase app '${appData.projectName}'`, `MIGRATE Updated getconfig target to use ignore environment directory for firebase app '${appData.projectName}'`, `MIGRATE Updated serve target for firebase app '${appData.projectName}'`, + `MIGRATE Added assets glob for firebase function app '${functionData.projectName}'`, `UPDATE firebase.json`, `CREATE ${appData.projectDir}/environment/.env`, `CREATE ${appData.projectDir}/environment/.env.local`, @@ -1167,7 +1160,30 @@ describe('nx-firebase e2e', () => { ]) validateProjectConfig(appData.projectDir, appData.projectName) -//todo: validateFunctionConfig + //todo: validateFunctionConfig + + + // run it again + const result3 = await syncGeneratorAsync(`--migrate`) + testDebug(result3.stdout) + expectStrings(result.stdout, [ + `Running plugin migrations for workspace`, + ]) + expectNoStrings(result3.stdout, [ + `MIGRATE Added default environment file 'environment/.env' for firebase app '${appData.projectName}'`, + `MIGRATE Added default environment file 'environment/.env.local' for firebase app '${appData.projectName}'`, + `MIGRATE Added default environment file 'environment/.secret.local' for firebase app '${appData.projectName}'`, + `MIGRATE Updated getconfig target to use ignore environment directory for firebase app '${appData.projectName}'`, + `MIGRATE Updated serve target for firebase app '${appData.projectName}'`, + `MIGRATE Added assets glob for firebase function app '${functionData.projectName}'`, + `UPDATE firebase.json`, + `CREATE ${appData.projectDir}/environment/.env`, + `CREATE ${appData.projectDir}/environment/.env.local`, + `CREATE ${appData.projectDir}/environment/.secret.local`, + `UPDATE ${appData.projectDir}/project.json`, + ]) + + // cleanup await cleanFunctionAsync(functionData) await cleanAppAsync(appData) From 32465aa6e539fc3dc738ca31625e2cfd3e6befeb Mon Sep 17 00:00:00 2001 From: Simon M Date: Fri, 22 Sep 2023 21:35:21 +0100 Subject: [PATCH 6/8] Lets use a generator for migrate instead --- e2e/nx-firebase-e2e/test-utils/index.ts | 4 +++ e2e/nx-firebase-e2e/tests/nx-firebase.spec.ts | 7 ++-- packages/nx-firebase/generators.json | 7 +++- .../{sync => migrate}/lib/migrate.ts | 2 +- .../src/generators/migrate/migrate.spec.ts | 24 +++++++++++++ .../src/generators/migrate/migrate.ts | 36 +++++++++++++++++++ .../src/generators/migrate/schema.d.ts | 2 ++ .../src/generators/migrate/schema.json | 9 +++++ .../nx-firebase/src/generators/sync/sync.ts | 2 +- 9 files changed, 87 insertions(+), 6 deletions(-) rename packages/nx-firebase/src/generators/{sync => migrate}/lib/migrate.ts (98%) create mode 100644 packages/nx-firebase/src/generators/migrate/migrate.spec.ts create mode 100644 packages/nx-firebase/src/generators/migrate/migrate.ts create mode 100644 packages/nx-firebase/src/generators/migrate/schema.d.ts create mode 100644 packages/nx-firebase/src/generators/migrate/schema.json diff --git a/e2e/nx-firebase-e2e/test-utils/index.ts b/e2e/nx-firebase-e2e/test-utils/index.ts index 06234a8f..ef2a1b5c 100644 --- a/e2e/nx-firebase-e2e/test-utils/index.ts +++ b/e2e/nx-firebase-e2e/test-utils/index.ts @@ -104,6 +104,10 @@ export async function syncGeneratorAsync(params: string = '') { return await safeRunNxCommandAsync(`g @simondotm/nx-firebase:sync ${params}`) } +export async function migrateGeneratorAsync(params: string = '') { + return await safeRunNxCommandAsync(`g @simondotm/nx-firebase:migrate ${params}`) +} + export async function libGeneratorAsync(projectData: ProjectData, params: string = '') { return await safeRunNxCommandAsync(`g @nx/js:lib ${projectData.name} ${params}`) } diff --git a/e2e/nx-firebase-e2e/tests/nx-firebase.spec.ts b/e2e/nx-firebase-e2e/tests/nx-firebase.spec.ts index e887bd94..7552c138 100644 --- a/e2e/nx-firebase-e2e/tests/nx-firebase.spec.ts +++ b/e2e/nx-firebase-e2e/tests/nx-firebase.spec.ts @@ -30,6 +30,7 @@ import { addImport, removeProjectAsync, renameProjectAsync, + migrateGeneratorAsync, } from '../test-utils' import { ProjectConfiguration, joinPathFragments } from '@nx/devkit' @@ -1101,16 +1102,16 @@ describe('nx-firebase e2e', () => { // Test migrations //-------------------------------------------------------------------------------------------------- - describe('nx-firebase sync migrate', () => { + describe('nx-firebase migrate', () => { it( - 'should successfuly sync migrate for legacy app', + 'should successfuly migrate for legacy app', async () => { const appData = getProjectData('apps', uniq('firebaseMigrateApp')) const functionData = getProjectData('apps', uniq('firebaseMigrateFunction')) await appGeneratorAsync(appData) await functionGeneratorAsync(functionData, `--app ${appData.projectName}`) - const result = await syncGeneratorAsync(`--migrate`) + const result = await migrateGeneratorAsync(``) testDebug(result.stdout) expectStrings(result.stdout, [ `Running plugin migrations for workspace`, diff --git a/packages/nx-firebase/generators.json b/packages/nx-firebase/generators.json index 0fc19fb4..af9fc1a1 100644 --- a/packages/nx-firebase/generators.json +++ b/packages/nx-firebase/generators.json @@ -25,6 +25,11 @@ "factory": "./src/generators/sync/sync", "schema": "./src/generators/sync/schema.json", "description": "nx-firebase project sync tool" - } + }, + "migrate": { + "factory": "./src/generators/migrate/migrate", + "schema": "./src/generators/migrate/schema.json", + "description": "nx-firebase project migration tool" + } } } diff --git a/packages/nx-firebase/src/generators/sync/lib/migrate.ts b/packages/nx-firebase/src/generators/migrate/lib/migrate.ts similarity index 98% rename from packages/nx-firebase/src/generators/sync/lib/migrate.ts rename to packages/nx-firebase/src/generators/migrate/lib/migrate.ts index de3b8aa4..eb1e263d 100644 --- a/packages/nx-firebase/src/generators/sync/lib/migrate.ts +++ b/packages/nx-firebase/src/generators/migrate/lib/migrate.ts @@ -5,7 +5,7 @@ import { updateProjectConfiguration, writeJson, } from '@nx/devkit' -import { FirebaseWorkspace } from './types' +import { FirebaseWorkspace } from '../../sync/lib/types' import { readFileSync } from 'fs' import { FirebaseFunction } from '../../../utils' diff --git a/packages/nx-firebase/src/generators/migrate/migrate.spec.ts b/packages/nx-firebase/src/generators/migrate/migrate.spec.ts new file mode 100644 index 00000000..1a7ca94a --- /dev/null +++ b/packages/nx-firebase/src/generators/migrate/migrate.spec.ts @@ -0,0 +1,24 @@ +import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing' +import { Tree, readProjectConfiguration } from '@nx/devkit' + +import generator from './migrate' +import { MigrateGeneratorSchema } from './schema' +import applicationGenerator from '../application/application' +import functionGenerator from '../function/function' + +// migrate is tested in e2e. + +describe('migrate generator', () => { + let tree: Tree + const options: MigrateGeneratorSchema = {} + + beforeEach(() => { + tree = createTreeWithEmptyWorkspace() + }) + + it('should run successfully', async () => { + await generator(tree, {}) + // const config = readProjectConfiguration(tree, 'test') + // expect(config).toBeDefined() + }) +}) diff --git a/packages/nx-firebase/src/generators/migrate/migrate.ts b/packages/nx-firebase/src/generators/migrate/migrate.ts new file mode 100644 index 00000000..64cc8f87 --- /dev/null +++ b/packages/nx-firebase/src/generators/migrate/migrate.ts @@ -0,0 +1,36 @@ +import { GeneratorCallback, logger, runTasksInSerial, Tree } from '@nx/devkit' + +import { MigrateGeneratorSchema } from './schema' +import initGenerator from '../init/init' + +import { debugInfo, getFirebaseWorkspace } from '../sync/lib' +import { runMigrations } from './lib/migrate' + +/** + * Migrate firebase workspace + * + */ +export default async function migrateGenerator( + tree: Tree, + options: MigrateGeneratorSchema, +): Promise { + const tasks: GeneratorCallback[] = [] + + // initialise plugin + const initTask = await initGenerator(tree, {}) + tasks.push(initTask) + + // otherwise, sync the workspace. + // build lists of firebase apps & functions that have been deleted or renamed + debugInfo('- Migrating workspace') + + const workspace = getFirebaseWorkspace(tree) + + logger.info( + `This workspace has ${workspace.firebaseAppProjects.size} firebase apps and ${workspace.firebaseFunctionProjects.size} firebase functions\n\n`, + ) + + runMigrations(tree, workspace) + + return runTasksInSerial(...tasks) +} diff --git a/packages/nx-firebase/src/generators/migrate/schema.d.ts b/packages/nx-firebase/src/generators/migrate/schema.d.ts new file mode 100644 index 00000000..a76ed709 --- /dev/null +++ b/packages/nx-firebase/src/generators/migrate/schema.d.ts @@ -0,0 +1,2 @@ +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface MigrateGeneratorSchema {} diff --git a/packages/nx-firebase/src/generators/migrate/schema.json b/packages/nx-firebase/src/generators/migrate/schema.json new file mode 100644 index 00000000..435d86aa --- /dev/null +++ b/packages/nx-firebase/src/generators/migrate/schema.json @@ -0,0 +1,9 @@ +{ + "$schema": "http://json-schema.org/schema", + "$id": "Migrate", + "title": "", + "type": "object", + "properties": { + }, + "required": [] +} diff --git a/packages/nx-firebase/src/generators/sync/sync.ts b/packages/nx-firebase/src/generators/sync/sync.ts index a1c67fdb..0c07ad1b 100644 --- a/packages/nx-firebase/src/generators/sync/sync.ts +++ b/packages/nx-firebase/src/generators/sync/sync.ts @@ -22,7 +22,7 @@ import { getFirebaseWorkspace, renameCommandForTarget, } from './lib' -import { runMigrations } from './lib/migrate' +import { runMigrations } from '../migrate/lib/migrate' const FUNCTIONS_DEPLOY_MATCHER = /(--only[ =]functions:)([^\s]+)/ From 0777c2c17148ed154c480823c47bb900878ef307 Mon Sep 17 00:00:00 2001 From: Simon M Date: Fri, 22 Sep 2023 22:44:06 +0100 Subject: [PATCH 7/8] Documentation updates --- CHANGELOG.md | 15 ++++-- README.md | 10 ++-- ...migration.md => nx-firebase-migrations.md} | 46 ++++++++++++++++--- 3 files changed, 57 insertions(+), 14 deletions(-) rename docs/{nx-firebase-v2-migration.md => nx-firebase-migrations.md} (58%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75edd869..8c2b1cd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,10 @@ All notable changes to this project will be documented in this file. +This plugin was completely rewritten since V2.x to use esbuild for bundling cloud functions. For documentation of the legacy v1.x plugin version see [here](https://github.com/simondotm/nx-firebase/tree/release/v1.1.0). + - [@simondotm/nx-firebase Changelog](#simondotmnx-firebase-changelog) + - [v2.1.0](#v210) - [v2.0.0](#v200) - [v2.0.0-beta.1](#v200-beta1) - [v2.0.0-beta.0](#v200-beta0) @@ -19,6 +22,14 @@ All notable changes to this project will be documented in this file. - [v0.2.3](#v023) - [v0.2.2 - Initial Release](#v022---initial-release) +## v2.1.0 + +* Added support for [environment variables](docs/nx-firebase-functions-environment.md) +* Added support for [secrets](docs/nx-firebase-functions-environment.md#environment-file-types) +* Added a custom `serve` executor that exits the Firebase Emulator suite properly +* Fixes to `sync` generator to update firebase app and firebase function project targets when they are renamed +* Added a custom `migrate` generator to ensure workspace configurations match the latest plugin version schemas + ## v2.0.0 Official v2 release @@ -35,7 +46,7 @@ Official v2 release Initial beta of plugin version 2.0. -Users of earlier plugin versions must read [here for plugin v1 -> v2 migration instructions](docs/nx-firebase-v2-migration.md) +Users of earlier plugin versions must read [here for plugin v1 -> v2 migration instructions](docs/nx-firebase-migrations.md) > **Please note that legacy v1 versions of the plugin are no longer supported from this point on, so only take this update if you are prepared to migrate your workspace and Firebase projects** @@ -50,8 +61,6 @@ Users of earlier plugin versions must read [here for plugin v1 -> v2 migration i * Minimum Nx version is now 16.1.1 * Watch mode build of function code & libraries is now fully supported when running Firebase emulator - - ## v1.1.0 No changes from beta. diff --git a/README.md b/README.md index 95f9922f..7d10c194 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,6 @@ A plugin for [Nx](https://nx.dev) v16.1.1+ that provides support for Firebase pr See [CHANGELOG](https://github.com/simondotm/nx-firebase/blob/main/CHANGELOG.md) for release notes. -This plugin was completely rewritten since V2.x to use esbuild for bundling cloud functions. For documentation of the legacy v1.x plugin version see [here](https://github.com/simondotm/nx-firebase/tree/release/v1.1.0). - ## Overview Nx provides a great way to manage monorepo workflows and this plugin helps make it easy to integrate Firebase projects with Nx. @@ -28,14 +26,13 @@ Features: # User Guide - **[Quick Start](docs/quick-start.md)** -- [Migrating from plugin v1.x to v2.x](docs/nx-firebase-v2-migration.md) +- [Migrating to new plugin versions](docs/nx-firebase-migrations.md) **Nx Firebase Generators** - [Firebase Applications](docs/nx-firebase-applications.md) - [Firebase Functions](docs/nx-firebase-functions.md) - [Firebase Functions - Environment Variables](docs/nx-firebase-functions-environment.md) -- [Firebase Sync](docs/nx-firebase-sync.md) **Nx Firebase** @@ -45,6 +42,11 @@ Features: - [Firebase Projects](docs/nx-firebase-projects.md) - [Project Schemas](docs/nx-firebase-project-structure.md) +**Nx Firebase Workspace Management** + +- [Nx-Firebase Sync](docs/nx-firebase-sync.md) + + **Nx Workspace** - [Nx Workspace Layout Ideas](docs/nx-workspace-layout.md) diff --git a/docs/nx-firebase-v2-migration.md b/docs/nx-firebase-migrations.md similarity index 58% rename from docs/nx-firebase-v2-migration.md rename to docs/nx-firebase-migrations.md index 444387fc..8d48c958 100644 --- a/docs/nx-firebase-v2-migration.md +++ b/docs/nx-firebase-migrations.md @@ -1,18 +1,50 @@ -# Migration to plugin v2.x from v1.x +# Nx-Firebase Plugin Migrations + +Newer versions of the plugin occasionally need to update the workspace configurations, and this page documents the strategies available across these versions. + +Please note that these migrations are provided on a 'best effort' basis, due to the fact that workspaces are quite complex and often customised. + +- [Nx-Firebase Plugin Migrations](#nx-firebase-plugin-migrations) + - [Migrating to plugin v2.1 from v2.0](#migrating-to-plugin-v21-from-v20) + - [Migration to plugin v2.x from v1.x](#migration-to-plugin-v2x-from-v1x) + - [1. Workspace Migration](#1-workspace-migration) + - [2. Firebase Project Migration](#2-firebase-project-migration) + - [3. Firebase Application Migration](#3-firebase-application-migration) + - [4. Firebase Functions Migration](#4-firebase-functions-migration) + - [5. Library updates](#5-library-updates) + - [6. Check Migration](#6-check-migration) + + + + +## Migrating to plugin v2.1 from v2.0 + +Plugin version 2.1 [added some new features](../CHANGELOG.md#v210) that required changes to the project configurations. + +To help with this & future updates, an automatic migration generator has been added: + +* Update to the latest plugin using `npm i @simondotm/nx-firebase@latest --save-dev` +* Run **npx nx g @simondotm/nx-firebase:migrate** + +This tool will run checks on your workspace firebase apps, functions and configurations and try to ensure they are correctly configured for compatibility with the plugin version you are using. + +> Please note, this generator is not the same as Nx's own migration tool, so always review the changes it makes to ensure they are appropriate for your workspace. + +## Migration to plugin v2.x from v1.x Version 2.x of this plugin has been completely rewritten, and uses a completely new approach to building & deploying, so migrating an existing Nx workspace using V1 of the plugin to use V2 requires some manual migration procedures. -## 1. Workspace Migration +### 1. Workspace Migration * First of all, your workspace will need to be migrated to at least Nx 16.1.1. * Next, update the `@simondotm/nx-firebase` plugin package to the latest v2.x version. -## 2. Firebase Project Migration +### 2. Firebase Project Migration Run the following steps 3-5 in order, for each separate Firebase application project in your workspace. -## 3. Firebase Application Migration +### 3. Firebase Application Migration * Next, you can either [generate a new firebase v2 application](./nx-firebase-applications.md) project and copy across your Firebase rules, indexes, storage rules etc. to the new firebase application project folder @@ -20,7 +52,7 @@ OR * you can manually modify your existing Firebase `project.json` to be structured [as shown here](./nx-firebase-project-structure.md#firebase-applications). -## 4. Firebase Functions Migration +### 4. Firebase Functions Migration If you are using Firebase functions in your project, the migration process is as follows: @@ -42,7 +74,7 @@ If you are using Firebase functions in your project, the migration process is as Check the [Nx-Firebase project schemas](./nx-firebase-project-structure.md) document for more information about the v2 plugin generators project layouts. -## 5. Library updates +### 5. Library updates The previous version of the plugin required that all Nx libraries imported by firebase functions were buildable. @@ -50,7 +82,7 @@ With v2 of the plugin, this is no longer the case and Nx libraries can be builda Nx Typescript libraries can be converted to non-buildable by simply removing the `build` target from their `project.json` files. -## 6. Check Migration +### 6. Check Migration Run `nx build your-firebase-project-name` to compile & bundle your functions. From 47da706bb37edd2abc29496f2bc636a8f43d2712 Mon Sep 17 00:00:00 2001 From: Simon M Date: Fri, 22 Sep 2023 23:13:56 +0100 Subject: [PATCH 8/8] More documentation --- CHANGELOG.md | 1 + README.md | 31 ++++++++++++++++++------------- packages/nx-firebase/README.md | 31 +++++++++++++++++-------------- 3 files changed, 36 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c2b1cd6..b0c12f1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ This plugin was completely rewritten since V2.x to use esbuild for bundling clou * Added a custom `serve` executor that exits the Firebase Emulator suite properly * Fixes to `sync` generator to update firebase app and firebase function project targets when they are renamed * Added a custom `migrate` generator to ensure workspace configurations match the latest plugin version schemas +* Updated plugin to be built against Nx 16.6.0 ## v2.0.0 diff --git a/README.md b/README.md index 7d10c194..74a0ba46 100644 --- a/README.md +++ b/README.md @@ -10,18 +10,23 @@ See [CHANGELOG](https://github.com/simondotm/nx-firebase/blob/main/CHANGELOG.md) Nx provides a great way to manage monorepo workflows and this plugin helps make it easy to integrate Firebase projects with Nx. -Features: - -* Supports single or multiple firebase projects/apps within an Nx workspace -* Generates Firebase application projects, with default `firebase.json` configurations, rules and indexes for each Firebase app -* Generates Firebase functions using customised Typescript Nx node applications -* Bundling of functions using `esbuild` for extremely fast compilation & tree-shaking for faster cold starts -* Easily import Typescript code libraries in your Firebase functions for code sharing -* Supports function environment variables and secrets -* Nx's automatic dependency checking for no-fuss builds, and per-project or per-function deployments -* Use the Firebase Emulator suite whilst developing locally - all functions are watched and updated live while you work -* Workspace management with the `sync` generator keeps your `firebase.json` configs automatically updated when renaming or deleting functions -* Only very lightly opinionated about your Firebase configurations and workspace layouts; you can use Nx or the Firebase CLI +### Features + +* **Firebase Apps** + * Generates Firebase application projects, with default `firebase.json` configurations, rules and indexes for each Firebase app +* **Firebase Functions** + * Generates Firebase function apps based on Typescript Nx node applications + * Bundling of Firebase functions using `esbuild` for extremely fast compilation & tree-shaking for optimal function cold starts + * Easily import Typescript Nx libraries from your Nx workspace into your Firebase functions for code sharing across projects + * Supports function environment variables and secrets +* **Firebase Features** + * Use the Firebase Emulator suite whilst developing locally - all functions are watched and updated live while you work + * Use Firebase hosting with Nx to easily build & deploy web apps +* **Workspace Management** + * Nx's automatic dependency checking for no-fuss builds, and per-project or per-function deployments + * Supports single or multiple firebase projects/apps within an Nx workspace + * Nx workspace management with the `sync` generator keeps your project & `firebase.json` configs automatically updated when renaming or deleting Firebase apps & functions + * Only very lightly opinionated about your Firebase configurations and workspace layouts; you can use Nx or the Firebase CLI # User Guide @@ -40,11 +45,11 @@ Features: - [Firebase Emulators](docs/nx-firebase-emulators.md) - [Firebase Databases](docs/nx-firebase-databases.md) - [Firebase Projects](docs/nx-firebase-projects.md) -- [Project Schemas](docs/nx-firebase-project-structure.md) **Nx Firebase Workspace Management** - [Nx-Firebase Sync](docs/nx-firebase-sync.md) +- [Nx-Firebase Project Schemas](docs/nx-firebase-project-structure.md) **Nx Workspace** diff --git a/packages/nx-firebase/README.md b/packages/nx-firebase/README.md index 18bd8267..63554860 100644 --- a/packages/nx-firebase/README.md +++ b/packages/nx-firebase/README.md @@ -4,24 +4,27 @@ A plugin for [Nx](https://nx.dev) v16.1.1+ that provides support for Firebase pr See [CHANGELOG](https://github.com/simondotm/nx-firebase/blob/main/CHANGELOG.md) for release notes. -This plugin was completely rewritten since V2.x to use esbuild for bundling cloud functions. For documentation of the legacy v1.x plugin version see [here](https://github.com/simondotm/nx-firebase/tree/release/v1.1.0). - ## Overview Nx provides a great way to manage monorepo workflows and this plugin helps make it easy to integrate Firebase projects with Nx. -Features: - -* Supports single or multiple firebase projects/apps within an Nx workspace -* Generates Firebase application projects, with default `firebase.json` configurations, rules and indexes for each Firebase app -* Generates Firebase functions using customised Typescript Nx node applications -* Bundling of functions using `esbuild` for extremely fast compilation & tree-shaking for faster cold starts -* Easily import Typescript code libraries in your Firebase functions for code sharing -* Supports function environment variables and secrets -* Nx's automatic dependency checking for no-fuss builds, and per-project or per-function deployments -* Use the Firebase Emulator suite whilst developing locally - all functions are watched and updated live while you work -* Workspace management with the `sync` generator keeps your `firebase.json` configs automatically updated when renaming or deleting functions -* Only very lightly opinionated about your Firebase configurations and workspace layouts; you can use Nx or the Firebase CLI +### Features + +* **Firebase Apps** + * Generates Firebase application projects, with default `firebase.json` configurations, rules and indexes for each Firebase app +* **Firebase Functions** + * Generates Firebase function apps based on Typescript Nx node applications + * Bundling of Firebase functions using `esbuild` for extremely fast compilation & tree-shaking for optimal function cold starts + * Easily import Typescript Nx libraries from your Nx workspace into your Firebase functions for code sharing across projects + * Supports function environment variables and secrets +* **Firebase Features** + * Use the Firebase Emulator suite whilst developing locally - all functions are watched and updated live while you work + * Use Firebase hosting with Nx to easily build & deploy web apps +* **Workspace Management** + * Nx's automatic dependency checking for no-fuss builds, and per-project or per-function deployments + * Supports single or multiple firebase projects/apps within an Nx workspace + * Nx workspace management with the `sync` generator keeps your project & `firebase.json` configs automatically updated when renaming or deleting Firebase apps & functions + * Only very lightly opinionated about your Firebase configurations and workspace layouts; you can use Nx or the Firebase CLI ## Further Information