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

test(create-gatsby): some for init starter #28376

Merged
merged 4 commits into from
Dec 1, 2020
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
162 changes: 162 additions & 0 deletions packages/create-gatsby/src/__tests__/init-starter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import { execSync } from "child_process"
import execa from "execa"
import fs from "fs-extra"
import path from "path"
import { initStarter } from "../init-starter"
import { reporter } from "../reporter"

jest.mock(`tiny-spin`, () => {
return {
spin: (): (() => void) => jest.fn(),
}
})
jest.mock(`../utils`)
jest.mock(`execa`)
jest.mock(`child_process`)
jest.mock(`fs-extra`)
jest.mock(`path`)
jest.mock(`../reporter`)
jest.mock(`../get-config-store`, () => {
return {
getConfigStore: (): unknown => {
return {
items: {},
set(key: string, value: unknown): void {
this.items[key] = value
},
get(key: string): unknown {
return this.items[key]
},

__reset(): void {
this.items = {}
},
}
},
}
})

describe(`init-starter`, () => {
beforeEach(() => {
process.chdir = jest.fn()
})

afterEach(() => {
jest.resetAllMocks()
})

describe(`initStarter / cloning`, () => {
it(`reports an error when it s not possible to clone the repo`, async () => {
;(path as any).join.mockImplementation(() => `/somewhere-here`)
;(execa as any).mockImplementation(() => {
throw new Error(`Not possible to clone the repo`)
})

try {
await initStarter(`gatsby-starter-hello-world`, `./somewhere`, [])
} catch (e) {
expect(execa).toBeCalledWith(`git`, [
`clone`,
`gatsby-starter-hello-world`,
`--recursive`,
`--depth=1`,
`--quiet`,
])
expect(reporter.panic).toBeCalledWith(`Not possible to clone the repo`)
expect(reporter.success).not.toBeCalledWith(
`Created site from template`
)
expect(fs.remove).toBeCalledWith(`/somewhere-here`)
}
})

it(`reports a success when everything is going ok`, async () => {
;(path as any).join.mockImplementation(() => `/somewhere-here`)
;(execa as any).mockImplementation(() => Promise.resolve())
;(fs as any).readJSON.mockImplementation(() => {
return { name: `gatsby-project` }
})

await initStarter(`gatsby-starter-hello-world`, `./somewhere`, [])

expect(execa).toBeCalledWith(`git`, [
`clone`,
`gatsby-starter-hello-world`,
`--recursive`,
`--depth=1`,
`--quiet`,
])
expect(reporter.panic).not.toBeCalled()
expect(reporter.success).toBeCalledWith(`Created site from template`)
expect(fs.remove).toBeCalledWith(`/somewhere-here`)
})
})

describe(`initStarter / install`, () => {
it(`process package installation with yarn`, async () => {
process.env.npm_config_user_agent = `yarn`
;(path as any).join.mockImplementation(() => `/somewhere-here`)
;(execa as any).mockImplementation(() => Promise.resolve())
;(fs as any).readJSON.mockImplementation(() => {
return { name: `gatsby-project` }
})

await initStarter(`gatsby-starter-hello-world`, `./somewhere`, [])

expect(fs.remove).toBeCalledWith(`package-lock.json`)
expect(reporter.success).toBeCalledWith(`Installed plugins`)
expect(reporter.panic).not.toBeCalled()
expect(execa).toBeCalledWith(`yarnpkg`, [`--silent`], {
stderr: `inherit`,
})
})

it(`process package installation with NPM`, async () => {
process.env.npm_config_user_agent = `npm`
;(path as any).join.mockImplementation(() => `/somewhere-here`)
;(execa as any).mockImplementation(() => Promise.resolve())
;(fs as any).readJSON.mockImplementation(() => {
return { name: `gatsby-project` }
})

await initStarter(`gatsby-starter-hello-world`, `./somewhere`, [
`one-package`,
])

expect(fs.remove).toBeCalledWith(`yarn.lock`)
expect(reporter.success).toBeCalledWith(`Installed Gatsby`)
expect(reporter.success).toBeCalledWith(`Installed plugins`)
expect(reporter.panic).not.toBeCalled()
expect(execa).toBeCalledWith(
`npm`,
[`install`, `--loglevel`, `error`, `--color`, `always`],
{ stderr: `inherit` }
)
expect(execa).toBeCalledWith(
`npm`,
[`install`, `--loglevel`, `error`, `--color`, `always`, `one-package`],
{ stderr: `inherit` }
)
})

it(`gently informs the user that yarn is not available when trying to use it`, async () => {
process.env.npm_config_user_agent = `yarn`
;(execSync as any).mockImplementation(() => {
throw new Error(`Something wrong occured when trying to use yarn`)
})
;(path as any).join.mockImplementation(() => `/somewhere-here`)
;(execa as any).mockImplementation(() => Promise.resolve())
;(fs as any).readJSON.mockImplementation(() => {
return { name: `gatsby-project` }
})

await initStarter(`gatsby-starter-hello-world`, `./somewhere`, [
`one-package`,
])

expect(reporter.info).toBeCalledWith(
`Woops! You have chosen "yarn" as your package manager, but it doesn't seem be installed on your machine. You can install it from https://yarnpkg.com/getting-started/install or change your preferred package manager with the command "gatsby options set pm npm". As a fallback, we will run the next steps with npm.`
)
})
})
})
62 changes: 32 additions & 30 deletions packages/create-gatsby/src/init-starter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { spin } from "tiny-spin"
import { getConfigStore } from "./get-config-store"
type PackageManager = "yarn" | "npm"
import c from "ansi-colors"
import { clearLine } from "./utils"

const packageManagerConfigKey = `cli.packageManager`

Expand All @@ -16,25 +17,25 @@ const kebabify = (str: string): string =>
.replace(/[^a-zA-Z]+/g, `-`)
.toLowerCase()

export const getPackageManager = (): PackageManager =>
getConfigStore().get(packageManagerConfigKey)
export const getPackageManager = (
npmConfigUserAgent?: string
): PackageManager => {
const configStore = getConfigStore()
const actualPackageManager = configStore.get(packageManagerConfigKey)

export const setPackageManager = (packageManager: PackageManager): void => {
getConfigStore().set(packageManagerConfigKey, packageManager)
}
if (actualPackageManager) {
return actualPackageManager
}

const ESC = `\u001b`
if (npmConfigUserAgent?.includes(`yarn`)) {
configStore.set(packageManagerConfigKey, `yarn`)
return `yarn`
}

configStore.set(packageManagerConfigKey, `npm`)
return `npm`
}

export const clearLine = (count = 1): Promise<boolean> =>
new Promise(resolve => {
// First move the cursor up one line...
process.stderr.moveCursor(0, -count, () => {
// ... then clear that line. This is the ANSI escape sequence for "clear whole line"
// List of escape sequences: http://ascii-table.com/ansi-escape-sequences.php
process.stderr.write(`${ESC}[2K`)
resolve()
})
})
// Checks the existence of yarn package
// We use yarnpkg instead of yarn to avoid conflict with Hadoop yarn
// Refer to https://github.com/yarnpkg/yarn/issues/673
Expand All @@ -43,6 +44,9 @@ const checkForYarn = (): boolean => {
execSync(`yarnpkg --version`, { stdio: `ignore` })
return true
} catch (e) {
reporter.info(
`Woops! You have chosen "yarn" as your package manager, but it doesn't seem be installed on your machine. You can install it from https://yarnpkg.com/getting-started/install or change your preferred package manager with the command "gatsby options set pm npm". As a fallback, we will run the next steps with npm.`
)
return false
}
}
Expand Down Expand Up @@ -117,36 +121,34 @@ const install = async (
const npmConfigUserAgent = process.env.npm_config_user_agent

try {
if (!getPackageManager()) {
if (npmConfigUserAgent?.includes(`yarn`)) {
setPackageManager(`yarn`)
} else {
setPackageManager(`npm`)
}
}
const pm = getPackageManager(npmConfigUserAgent)

const options: Options = {
stderr: `inherit`,
}

const config = [`--loglevel`, `error`, `--color`, `always`]

if (getPackageManager() === `yarn` && checkForYarn()) {
await fs.remove(`package-lock.json`)
if (pm === `yarn` && checkForYarn()) {
const args = packages.length
? [`add`, `--silent`, ...packages]
: [`--silent`]

await fs.remove(`package-lock.json`)
await execa(`yarnpkg`, args, options)
} else {
await fs.remove(`yarn.lock`)

await execa(`npm`, [`install`, ...config], options)
await clearLine()

reporter.success(`Installed Gatsby`)
reporter.info(`${c.blueBright(c.symbols.pointer)} Installing plugins...`)

await execa(`npm`, [`install`, ...config, ...packages], options)
await clearLine()
reporter.success(`Installed plugins`)
}

reporter.success(`Installed plugins`)
} catch (e) {
reporter.panic(e.message)
} finally {
Expand All @@ -161,9 +163,7 @@ const clone = async (
branch?: string
): Promise<void> => {
const branchProps = branch ? [`-b`, branch] : []

const stop = spin(`Cloning site template`)

const args = [
`clone`,
...branchProps,
Expand All @@ -176,11 +176,13 @@ const clone = async (

try {
await execa(`git`, args)

reporter.success(`Created site from template`)
} catch (err) {
reporter.panic(err.message)
}

stop()
reporter.success(`Created site from template`)
await fs.remove(path.join(rootPath, `.git`))
}

Expand Down
12 changes: 12 additions & 0 deletions packages/create-gatsby/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const ESC = `\u001b`

export const clearLine = (count = 1): Promise<boolean> =>
new Promise(resolve => {
// First move the cursor up one line...
process.stderr.moveCursor(0, -count, () => {
// ... then clear that line. This is the ANSI escape sequence for "clear whole line"
// List of escape sequences: http://ascii-table.com/ansi-escape-sequences.php
process.stderr.write(`${ESC}[2K`)
resolve()
})
})