Skip to content
Draft
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
18 changes: 14 additions & 4 deletions src/commands/watch/watch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import prettyjson from 'prettyjson'
import { type Spinner, startSpinner, stopSpinner } from '../../lib/spinner.js'
import { chalk, logAndThrowError, log } from '../../utils/command-helpers.js'
import type BaseCommand from '../base-command.js'
import type { BaseOptionValues } from '../base-command.js'
import { init } from '../init/init.js'

// 1 second
Expand All @@ -15,7 +16,7 @@ const BUILD_FINISH_INTERVAL = 1e3
// 20 minutes
const BUILD_FINISH_TIMEOUT = 12e5

const waitForBuildFinish = async function (api: NetlifyAPI, siteId: string, spinner: Spinner) {
const waitForBuildFinish = async function (api: NetlifyAPI, siteId: string, spinner?: Spinner) {
let firstPass = true

const waitForBuildToFinish = async function () {
Expand All @@ -27,7 +28,9 @@ const waitForBuildFinish = async function (api: NetlifyAPI, siteId: string, spin
// @TODO implement build error messages into this

if (!currentBuilds || currentBuilds.length === 0) {
stopSpinner({ spinner })
if (spinner) {
stopSpinner({ spinner })
}
return true
}
firstPass = false
Expand All @@ -46,7 +49,7 @@ const waitForBuildFinish = async function (api: NetlifyAPI, siteId: string, spin
return firstPass
}

export const watch = async (_options: unknown, command: BaseCommand) => {
export const watch = async (options: BaseOptionValues, command: BaseCommand) => {
await command.authenticate()
const client = command.netlify.api
let siteId = command.netlify.site.id
Expand All @@ -57,6 +60,8 @@ export const watch = async (_options: unknown, command: BaseCommand) => {
siteId = siteData.id
}

const isSilent = options.silent

// wait for 1 sec for everything to kickoff
console.time('Deploy time')
await new Promise((resolve) => {
Expand All @@ -80,7 +85,12 @@ export const watch = async (_options: unknown, command: BaseCommand) => {
// "created_at": "2018-07-17T17:14:03.423Z"
// }
//
const spinner = startSpinner({ text: 'Waiting for active project deploys to complete' })

let spinner
if (!isSilent) {
spinner = startSpinner({ text: 'Waiting for active project deploys to complete' })
}

try {
// Fetch all builds!
// const builds = await client.listSiteBuilds({siteId})
Expand Down
144 changes: 144 additions & 0 deletions tests/integration/commands/watch/watch.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import process from 'process'

import { afterAll, afterEach, beforeEach, describe, expect, test, vi, type MockInstance } from 'vitest'

import { getEnvironmentVariables, withMockApi } from '../../utils/mock-api.js'
import type { MinimalAccount } from '../../../../src/utils/types.js'
import { startSpinner } from '../../../../src/lib/spinner.js'

vi.mock('../../../../src/lib/spinner.js', async () => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
const realStartSpinner = (await vi.importActual(
'../../../../src/lib/spinner.js',
)) as typeof import('../../../../src/lib/spinner.js')

return {
...realStartSpinner,
startSpinner: vi.fn(() => ({ stop: vi.fn(), error: vi.fn(), clear: vi.fn() })),
}
})

const siteInfo = {
account_slug: 'test-account',
id: 'site_id',
name: 'site-name',
admin_url: 'https://app.netlify.com/projects/test-site/overview',
url: 'https://test-site.netlify.app/',
ssl_url: 'https://test-site.netlify.app/',
}

const user = { full_name: 'Test User', email: 'test@netlify.com' }

const accounts: MinimalAccount[] = [
{
id: 'user-id',
name: user.full_name,
slug: siteInfo.account_slug,
default: true,
team_logo_url: null,
on_pro_trial: false,
organization_id: null,
type_name: 'placeholder',
type_slug: 'placeholder',
members_count: 1,
},
]

const routes = [
{ path: 'sites/site_id', response: siteInfo },
{ path: 'sites/site_id/builds', response: [] },
{ path: 'accounts', response: accounts },
]

const importModules = async () => {
const { default: BaseCommand } = await import('../../../../src/commands/base-command.js')
const { createWatchCommand } = await import('../../../../src/commands/watch/index.js')

return { BaseCommand, createWatchCommand }
}

const OLD_ENV = process.env
const OLD_ARGV = process.argv

describe('watch command', () => {
let stdoutSpy: MockInstance

beforeEach(() => {
vi.clearAllMocks()
vi.resetModules()
Object.defineProperty(process, 'env', { value: {} })
stdoutSpy = vi.spyOn(process.stdout, 'write').mockImplementation(() => true)
})

afterEach(() => {
stdoutSpy.mockRestore()
process.argv = OLD_ARGV
vi.resetModules()
})

afterAll(() => {
vi.restoreAllMocks()
Object.defineProperty(process, 'env', { value: OLD_ENV })
})

test('should start spinner when --silent flag is not passed', async () => {
process.argv = ['node', 'netlify', 'watch']
const { BaseCommand, createWatchCommand } = await importModules()

await withMockApi(routes, async ({ apiUrl }) => {
Object.assign(process.env, getEnvironmentVariables({ apiUrl }))

const program = new BaseCommand('netlify')
createWatchCommand(program)
await program.parseAsync(['', '', 'watch'])

expect(startSpinner).toHaveBeenCalledOnce()
expect(startSpinner).toHaveBeenCalledWith({ text: 'Waiting for active project deploys to complete' })
})
})

test('should not start spinner when --silent flag is passed', async () => {
process.argv = ['node', 'netlify', 'watch', '--silent']
const { BaseCommand, createWatchCommand } = await importModules()

await withMockApi(routes, async ({ apiUrl }) => {
Object.assign(process.env, getEnvironmentVariables({ apiUrl }))

const program = new BaseCommand('netlify')
createWatchCommand(program)
await program.parseAsync(['', '', 'watch', '--silent'])

expect(startSpinner).not.toHaveBeenCalled()
})
})

test('should not print to stdout when --silent flag is passed', async () => {
process.argv = ['node', 'netlify', 'watch', '--silent']
const { BaseCommand, createWatchCommand } = await importModules()

await withMockApi(routes, async ({ apiUrl }) => {
Object.assign(process.env, getEnvironmentVariables({ apiUrl }))

const program = new BaseCommand('netlify')
createWatchCommand(program)
await program.parseAsync(['', '', 'watch', '--silent'])

expect(stdoutSpy).not.toHaveBeenCalled()
})
})

test('should allow output to stdout when --silent flag is not passed', async () => {
process.argv = ['node', 'netlify', 'watch']
const { BaseCommand, createWatchCommand } = await importModules()

await withMockApi(routes, async ({ apiUrl }) => {
Object.assign(process.env, getEnvironmentVariables({ apiUrl }))

const program = new BaseCommand('netlify')
createWatchCommand(program)
await program.parseAsync(['', '', 'watch'])

expect(stdoutSpy).toHaveBeenCalledTimes(3)
})
})
})