Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Enhance compatibility tests #201

Merged
merged 3 commits into from
Mar 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions e2e/compat/src/app/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,7 @@ export async function setupNxWorkspace(cache: Cache, force = false) {

ensureDir(cache.rootDir)

setLogFile(`${cache.rootDir}/${cache.nxVersion}.log.txt`)

log(
info(
`Creating new Nx workspace version ${cache.nxVersion} in directory '${cache.testDir}'`,
)

Expand All @@ -55,7 +53,7 @@ export async function setupNxWorkspace(cache: Cache, force = false) {
deleteDir(cache.testDir)
} else {
log(
`WQokspace archive '${cache.archiveFile}' already exists for '${cache.workspaceDir}', no setup required`,
`Workspace archive '${cache.archiveFile}' already exists for '${cache.workspaceDir}', no setup required`,
)
}
info(green(`SETUP VERSION '${cache.nxVersion}' SUCCEEDED\n`))
Expand Down
123 changes: 99 additions & 24 deletions e2e/compat/src/app/test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import { Cache, getCache } from './utils/cache'
import { get } from 'http'
import { Cache, getCache, isNxVersionSince } from './utils/cache'
import { customExec, runNxCommandAsync } from './utils/exec'
import { expectToContain, expectToNotContain, it } from './utils/jest-ish'
import { green, info, red, setLogFile, time } from './utils/log'
import { addContentToTextFile, deleteDir, setCwd } from './utils/utils'
import { green, info, log, red, setLogFile, time } from './utils/log'
import {
addContentToTextFile,
deleteDir,
getFileSize,
setCwd,
} from './utils/utils'
import { installPlugin } from './workspace'

const npmContent = [
Expand All @@ -16,37 +22,104 @@ const importMatch = `import * as functions from "firebase-functions";`

const notCachedMatch = `[existing outputs match the cache, left as is]`

export async function testPlugin(workspaceDir: string) {
const DELETE_AFTER_TEST = false

/**
* A basic e2e test suite for the plugin to check compatibility with different Nx versions
* We just want to check that the plugin can generate and build firebase apps and functions in each nx workspace version
* @param cache
*/
export async function testPlugin(cache: Cache) {
const workspaceDir = cache.workspaceDir
const indexTsPath = `${workspaceDir}/apps/functions/src/index.ts`

await runNxCommandAsync('g @simondotm/nx-firebase:app functions')
// from nx 16.8.0, apps and libs dirs need to be specified in the commandline
let appsDirectory = ''
let libsDirectory = ''
if (isNxVersionSince(cache, '16.8.0')) {
appsDirectory = '--directory=apps'
libsDirectory = '--directory=libs'
}

// the function generator imports @nx/node, which is only installed to the workspace if the app is generated first
// so this test checks that the workspace is setup correctly
await it('should throw if function is generated before app', async () => {
let failed = false
try {
await runNxCommandAsync(
`g @simondotm/nx-firebase:func functions ${appsDirectory} --app=firebase`,
)
} catch (err) {
failed = true
}

expectToContain(failed ? 'failed' : 'succeeded', 'failed')
})

// generate a test firebase app
await runNxCommandAsync(
`g @simondotm/nx-firebase:app firebase ${appsDirectory}`,
)
// generate a test firebase function
await runNxCommandAsync(
'g @nx/js:lib lib1 --buildable --importPath="@myorg/lib1"',
`g @simondotm/nx-firebase:func functions ${appsDirectory} --app=firebase`,
)
// generate a test js library
await runNxCommandAsync(
`g @nx/js:lib lib1 ${libsDirectory} --importPath="@myorg/lib1"`,
)

await it('should build the lib', async () => {
await runNxCommandAsync('build lib1')
// await it('should build the lib', async () => {
// await runNxCommandAsync('build lib1')
// })

// build the firebase app
await it('should build the firebase app', async () => {
const { stdout } = await runNxCommandAsync('build firebase')
// expectToNotContain(stdout, npmContent)
// expectToNotContain(stdout, libContent)
})

await it('should build the functions', async () => {
// build the firebase functions
await it('should build the functions app', async () => {
const { stdout } = await runNxCommandAsync('build functions')
expectToNotContain(stdout, npmContent)
expectToNotContain(stdout, libContent)
log(stdout)
})

await it('should update index.ts so that deps are updated after creation', async () => {
addContentToTextFile(indexTsPath, importMatch, '// comment added')
const { stdout } = await runNxCommandAsync('build functions')
expectToContain(stdout, npmContent)
expectToNotContain(stdout, libContent)
// check that sync runs
await it('should sync the workspace', async () => {
const { stdout } = await runNxCommandAsync('g @simondotm/nx-firebase:sync')
expectToContain(
stdout,
`This workspace has 1 firebase apps and 1 firebase functions`,
)
log(stdout)
})

await it('should add a lib dependency', async () => {
const importAddition = `import { lib1 } from '@myorg/lib1'\nconsole.log(lib1())\n`
addContentToTextFile(indexTsPath, importMatch, importAddition)
const { stdout } = await runNxCommandAsync('build functions')
expectToContain(stdout, npmContent)
expectToContain(stdout, libContent)
// await it('should update index.ts so that deps are updated after creation', async () => {
// addContentToTextFile(indexTsPath, importMatch, '// comment added')
// const { stdout } = await runNxCommandAsync('build functions')
// expectToContain(stdout, npmContent)
// expectToNotContain(stdout, libContent)
// })

// await it('should add a lib dependency', async () => {
// const importAddition = `import { lib1 } from '@myorg/lib1'\nconsole.log(lib1())\n`
// addContentToTextFile(indexTsPath, importMatch, importAddition)
// const { stdout } = await runNxCommandAsync('build functions')
// expectToContain(stdout, npmContent)
// expectToContain(stdout, libContent)
// })

// some early 16.x versions of nx seem to have a flaky esbuild implementation
// that intermittently fails to exclude external deps from the bundle
// we check for this by testing the bundle size is not >1kb
await it('should not bundle external deps', async () => {
const fileSize = getFileSize(`${workspaceDir}/dist/apps/functions/main.js`)
if (fileSize > 1024)
throw new Error(
`TEST FAILED: esbuild bundle size is >1kb (${fileSize / 1024}kb)`,
)
})

// TODO: other checks
Expand Down Expand Up @@ -94,7 +167,7 @@ export async function testNxVersion(cache: Cache) {
}

// run the plugin test suite
await testPlugin(cache.workspaceDir)
await testPlugin(cache)

info(green(`TESTING VERSION '${cache.nxVersion}' SUCCEEDED\n`))
} catch (err) {
Expand All @@ -116,7 +189,9 @@ export async function testNxVersion(cache: Cache) {
// cleanup
setCwd(cache.rootDir)

deleteDir(cache.testDir)
if (DELETE_AFTER_TEST) {
deleteDir(cache.testDir)
}

const dt = Date.now() - t
info(`Completed in ${time(dt)}\n`)
Expand Down
22 changes: 20 additions & 2 deletions e2e/compat/src/app/utils/cache.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { defaultCwd } from './cwd'
import { info } from './log'
import { satisfies } from 'semver'

export const localPluginVersion = 'local'

// const CACHE_DIR = `${defaultCwd}/node_modules/.cache/nx-firebase`
// const CACHE_DIR = `${defaultCwd}/.nx-firebase`
const CACHE_DIR = `${defaultCwd}/../.nx-firebase`

export type Cache = {
nxVersion: string
pluginVersion: string
Expand All @@ -12,7 +18,7 @@ export type Cache = {
pluginWorkspace: string
disableDaemon: boolean
isLocalPlugin: boolean
deferPluginInstall: boolean // defer plugin installs to the test suite rather than the workspace setup
deferPluginInstall: boolean // defer plugin installs during each test suite rather than in the workspace setup
nodeVersion: number // major node version
}

Expand All @@ -23,7 +29,10 @@ export type Cache = {
* @returns
*/
export function getCache(nxVersion: string, pluginVersion: string): Cache {
const rootDir = `${defaultCwd}/node_modules/.cache/nx-firebase/${pluginVersion}`
info(
`getting Cache for nxVersion=${nxVersion} pluginVersion=${pluginVersion}, using cache dir '${CACHE_DIR}'`,
)
const rootDir = `${CACHE_DIR}/${pluginVersion}`
const testDir = `${rootDir}/${nxVersion}`
const archiveFile = `${rootDir}/${nxVersion}.tar.gz`
const workspaceDir = `${testDir}/myorg`
Expand All @@ -45,3 +54,12 @@ export function getCache(nxVersion: string, pluginVersion: string): Cache {
nodeVersion: parseInt(process.versions.node.split('.')[0]),
}
}

export function isNxVersionSince(cache: Cache, nxVersion: string) {
console.log(
`checking isNxVersionSince satisfies ${cache.nxVersion} >= ${nxVersion}`,
)
const isOk = satisfies(cache.nxVersion, `>=${nxVersion}`)
console.log('isNxVersionSince check returned ', isOk)
return isOk
}
7 changes: 5 additions & 2 deletions e2e/compat/src/app/utils/exec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { info } from './log'
import { log } from './log'
import { exec } from 'child_process'

Expand All @@ -13,9 +14,10 @@ export async function customExec(
): Promise<{ stdout: string; stderr: string }> {
const cwd = dir ? dir : process.cwd()
return new Promise((resolve, reject) => {
log(`Executing command '${command}' in '${cwd}'`)
info(`Executing command '${command}' in '${cwd}'`)
const process = exec(
command,
{ cwd: cwd },
// { cwd: cwd, env: { NX_DAEMON: 'false' } }, // force CI type environment so Nx Daemon doesn't act up with multiple instances
// { cwd: cwd, env: { CI: 'true' } }, // force CI type environment so Nx Daemon doesn't act up with multiple instances
(error, stdout, stderr) => {
Expand Down Expand Up @@ -44,6 +46,7 @@ export async function customExec(
}

export async function runNxCommandAsync(command: string, dir?: string) {
const result = await customExec(`npx nx ${command} --verbose`, dir)
const cmd = `npx nx ${command} --verbose`
const result = await customExec(cmd, dir)
return result
}
3 changes: 2 additions & 1 deletion e2e/compat/src/app/utils/jest-ish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ export function expectToNotContain(

// hacky jest-like tester
export async function it(testName: string, testFunc: () => Promise<void>) {
log(` - ${testName}`)
info(` - it ${testName}`)
log(` - it ${testName}`)
try {
await testFunc()
} catch (err) {
Expand Down
15 changes: 12 additions & 3 deletions e2e/compat/src/app/utils/log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,20 @@ const ENABLE_LOG = false
const DEFAULT_LOG_FILE = `${process.cwd()}/e2e.log`

import * as fs from 'fs'
import { ensureDir } from './utils'

let LOG_FILE: string | undefined

function writeLog(msg: string) {
ensureDir(LOG_FILE)
fs.appendFileSync(LOG_FILE, `${msg}\n`)
}

export function setLogFile(path?: string) {
LOG_FILE = path || DEFAULT_LOG_FILE
fs.writeFileSync(LOG_FILE, '')
console.log(`Logging to '${LOG_FILE}'`)
ensureDir(LOG_FILE)
fs.writeFileSync(LOG_FILE, '') // reset log file
}

setLogFile()
Expand All @@ -16,12 +24,13 @@ export function log(msg: string) {
if (ENABLE_LOG) {
console.log(msg)
}
fs.appendFileSync(LOG_FILE, `${msg}\n`)
writeLog(msg)
}

export function info(msg: string) {
console.log(msg)
fs.appendFileSync(LOG_FILE, `${msg}\n`)
writeLog(msg)
// fs.appendFileSync(LOG_FILE, `${msg}\n`)
}

export function time(ms: number) {
Expand Down
18 changes: 15 additions & 3 deletions e2e/compat/src/app/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as fs from 'fs'
// import { readJsonFile, writeJsonFile } from '@nx/devkit'
// import { exit } from 'process'
import { log } from './log'
import { dirname } from 'path'

/**
* Set current working directory
Expand All @@ -15,11 +16,17 @@ export function setCwd(dir: string) {
log(`Switched cwd to '${process.cwd()}'`)
}

/**
* Ensure given directory path to filename or dir exists, create if it doesn't
* @param path - filename or directory path
* @returns true if path already exists
*/
export function ensureDir(path: string) {
const pathExists = fs.existsSync(path)
const dir = dirname(path)
const pathExists = fs.existsSync(dir)
if (!pathExists) {
log(`Creating dir '${path}'...`)
fs.mkdirSync(path, { recursive: true })
console.log(`Creating dir '${dir}'...`)
fs.mkdirSync(dir, { recursive: true })
}
return pathExists
}
Expand Down Expand Up @@ -58,3 +65,8 @@ export function addContentToTextFile(
const replaced = content.replace(match, `${match}\n${addition}`)
fs.writeFileSync(path, replaced)
}

export function getFileSize(path: string) {
const stats = fs.statSync(path)
return stats.size
}
Loading
Loading