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

Add destroy command #487

Merged
merged 31 commits into from
May 25, 2020
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
f9ce118
WIP: add destroy for components (#80)
antonmoiseev Apr 30, 2020
f25c9a2
WIP: fix import path
antonmoiseev Apr 30, 2020
43d66cc
WIP: move destroy component command's file into subfolder.
antonmoiseev Apr 30, 2020
79a93b5
WIP: add `destroy service` command
antonmoiseev Apr 30, 2020
385ffe3
WIP: add destroy for cell and layout components
antonmoiseev May 1, 2020
b1956c2
WIP: improve desc for generate and destroy tasks
antonmoiseev May 1, 2020
b0a2f7b
WIP: add destroy for page components
antonmoiseev May 1, 2020
f5a1316
WIP: add destroy for sdl components
antonmoiseev May 1, 2020
e8a426e
WIP: add destroy for scaffold components
antonmoiseev May 2, 2020
bd9b5b6
WIP: clean up empty dirs after destroy
antonmoiseev May 2, 2020
ffe5fc2
Minor cleanup, renaming
antonmoiseev May 3, 2020
4bf03fb
Add test for destroy page command
antonmoiseev May 11, 2020
dd1a6b4
Split page destroy test into three
antonmoiseev May 12, 2020
13581a0
Merge branch 'master' into issue80
antonmoiseev May 18, 2020
f4ac09c
Adjust scaffold destroy to #423 changes
antonmoiseev May 18, 2020
ae7037c
Add test for destroy component command
antonmoiseev May 18, 2020
4b54fc3
Add test for destroy cell command
antonmoiseev May 18, 2020
a412fe4
Add test for destroy layout command
antonmoiseev May 18, 2020
db31736
Add test for destroy service command
antonmoiseev May 18, 2020
92f2c31
Add test for destroy scaffold command
antonmoiseev May 19, 2020
864243e
Merge branch 'master' into issue80
antonmoiseev May 19, 2020
e4bd971
Add test for destroy sdl command
antonmoiseev May 19, 2020
2ca9be8
Add destroy function command
antonmoiseev May 19, 2020
49b8568
Add test for destroy function command
antonmoiseev May 19, 2020
d99e469
Merge branch 'master' into issue80
antonmoiseev May 20, 2020
b3475f2
Fix destroy scaffold after removing --path flag
antonmoiseev May 20, 2020
6f7f2d2
Merge branch 'master' into issue80
antonmoiseev May 20, 2020
f5685fa
Merge branch 'master' into issue80
antonmoiseev May 20, 2020
b183ff7
Merge branch 'master' into issue80
antonmoiseev May 20, 2020
93d3b82
Merge branch 'master' into issue80
peterp May 25, 2020
18f29d7
Remove `argv` to make command work.
peterp May 25, 2020
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
34 changes: 34 additions & 0 deletions packages/cli/__mocks__/fs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { isString } from 'lodash'

const fs = {
...require.requireActual('fs'),
}
Comment on lines +3 to +5
Copy link
Contributor Author

@antonmoiseev antonmoiseev May 11, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried this instead:

const fs = jest.genMockFromModule('fs')

But it breaks in a weird way:

image


// This is a custom function that our tests can use during setup to specify
// what the files on the "mock" filesystem should look like when any of the
// `fs` APIs are used.
let mockFiles = {}
/** @param newMockFiles - {[filepath]: contents} */
fs.__setMockFiles = (newMockFiles) => {
mockFiles = { ...newMockFiles }
}

fs.existsSync = (path) => {
return isString(mockFiles[path])
}

fs.mkdirSync = () => {}

fs.readFileSync = (path) => {
return mockFiles[path]
}

fs.writeFileSync = (path, contents) => {
mockFiles[path] = contents
}

fs.unlinkSync = (path) => {
delete mockFiles[path]
}

module.exports = fs
6 changes: 6 additions & 0 deletions packages/cli/src/commands/destroy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const command = 'destroy <type>'
export const aliases = ['d']
export const desc = 'Rollback changes made by generate command.'
peterp marked this conversation as resolved.
Show resolved Hide resolved

export const builder = (yargs) =>
yargs.commandDir('./destroy', { recurse: true }).demandCommand().argv
peterp marked this conversation as resolved.
Show resolved Hide resolved
36 changes: 36 additions & 0 deletions packages/cli/src/commands/destroy/cell/__tests__/cell.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
global.__dirname = __dirname
jest.mock('fs')
jest.mock('src/lib', () => {
return {
...require.requireActual('src/lib'),
generateTemplate: () => '',
}
})

import fs from 'fs'

import 'src/lib/test'

import { files } from '../../../generate/cell/cell'
import { tasks } from '../cell'

beforeEach(() => {
fs.__setMockFiles(files({ name: 'User' }))
})

afterEach(() => {
fs.__setMockFiles({})
jest.spyOn(fs, 'unlinkSync').mockClear()
})

test('destroys cell files', async () => {
const unlinkSpy = jest.spyOn(fs, 'unlinkSync')
const t = tasks({ componentName: 'cell', filesFn: files, name: 'User' })
t.setRenderer('silent')

return t.run().then(() => {
const generatedFiles = Object.keys(files({ name: 'User' }))
expect(generatedFiles.length).toEqual(unlinkSpy.mock.calls.length)
generatedFiles.forEach((f) => expect(unlinkSpy).toHaveBeenCalledWith(f))
})
})
13 changes: 13 additions & 0 deletions packages/cli/src/commands/destroy/cell/cell.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { files as cellFiles } from '../../generate/cell/cell'
import { createYargsForComponentDestroy } from '../helpers'

export const {
command,
desc,
builder,
handler,
tasks,
} = createYargsForComponentDestroy({
componentName: 'cell',
filesFn: cellFiles,
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
global.__dirname = __dirname
jest.mock('fs')
jest.mock('src/lib', () => {
return {
...require.requireActual('src/lib'),
generateTemplate: () => '',
}
})

import fs from 'fs'

import 'src/lib/test'

import { files } from '../../../generate/component/component'
import { tasks } from '../component'

beforeEach(() => {
fs.__setMockFiles(files({ name: 'About' }))
})

afterEach(() => {
fs.__setMockFiles({})
jest.spyOn(fs, 'unlinkSync').mockClear()
})

test('destroys component files', async () => {
const unlinkSpy = jest.spyOn(fs, 'unlinkSync')
const t = tasks({ componentName: 'component', filesFn: files, name: 'About' })
t.setRenderer('silent')

return t.run().then(() => {
const generatedFiles = Object.keys(files({ name: 'About' }))
expect(generatedFiles.length).toEqual(unlinkSpy.mock.calls.length)
generatedFiles.forEach((f) => expect(unlinkSpy).toHaveBeenCalledWith(f))
})
})
14 changes: 14 additions & 0 deletions packages/cli/src/commands/destroy/component/component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { files as componentFiles } from '../../generate/component/component'
import { createYargsForComponentDestroy } from '../helpers'

export const desc = 'Destroy a component.'

export const {
command,
builder,
handler,
tasks,
} = createYargsForComponentDestroy({
componentName: 'component',
filesFn: componentFiles,
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
global.__dirname = __dirname
jest.mock('fs')
jest.mock('src/lib', () => {
return {
...require.requireActual('src/lib'),
generateTemplate: () => '',
}
})

import fs from 'fs'

import 'src/lib/test'

import { files } from '../../../generate/function/function'
import { tasks } from '../function'

beforeEach(async () => {
fs.__setMockFiles(await files({ name: 'sendMail' }))
})

afterEach(() => {
fs.__setMockFiles({})
jest.spyOn(fs, 'unlinkSync').mockClear()
})

test('destroys service files', async () => {
const unlinkSpy = jest.spyOn(fs, 'unlinkSync')
const t = tasks({
componentName: 'service',
filesFn: files,
name: 'sendMail',
})
t.setRenderer('silent')

return t.run().then(async () => {
const generatedFiles = Object.keys(await files({ name: 'sendMail' }))
expect(generatedFiles.length).toEqual(unlinkSpy.mock.calls.length)
generatedFiles.forEach((f) => expect(unlinkSpy).toHaveBeenCalledWith(f))
})
})
14 changes: 14 additions & 0 deletions packages/cli/src/commands/destroy/function/function.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { files as functionFiles } from '../../generate/function/function'
import { createYargsForComponentDestroy } from '../helpers'

export const desc = 'Destroy a function'

export const {
builder,
command,
handler,
tasks,
} = createYargsForComponentDestroy({
componentName: 'function',
filesFn: functionFiles,
})
35 changes: 35 additions & 0 deletions packages/cli/src/commands/destroy/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import Listr from 'listr'

import { deleteFilesTask } from 'src/lib'
import c from 'src/lib/colors'

const tasks = ({ componentName, filesFn, name }) =>
new Listr(
[
{
title: `Destroying ${componentName} files...`,
task: async () => {
const f = await filesFn({ name })
return deleteFilesTask(f)
},
},
],
{ collapse: false, exitOnError: true }
)

export const createYargsForComponentDestroy = ({ componentName, filesFn }) => {
return {
command: `${componentName} <name>`,
desc: `Destroy a ${componentName} component.`,
handler: async ({ name }) => {
const t = tasks({ componentName, filesFn, name })

try {
await t.run()
} catch (e) {
console.log(c.error(e.message))
}
},
tasks,
}
}
36 changes: 36 additions & 0 deletions packages/cli/src/commands/destroy/layout/__tests__/layout.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
global.__dirname = __dirname
jest.mock('fs')
jest.mock('src/lib', () => {
return {
...require.requireActual('src/lib'),
generateTemplate: () => '',
}
})

import fs from 'fs'

import 'src/lib/test'

import { files } from '../../../generate/layout/layout'
import { tasks } from '../layout'

beforeEach(() => {
fs.__setMockFiles(files({ name: 'Blog' }))
})

afterEach(() => {
fs.__setMockFiles({})
jest.spyOn(fs, 'unlinkSync').mockClear()
})

test('destroys layout files', async () => {
const unlinkSpy = jest.spyOn(fs, 'unlinkSync')
const t = tasks({ componentName: 'layout', filesFn: files, name: 'Blog' })
t.setRenderer('silent')

return t.run().then(() => {
const generatedFiles = Object.keys(files({ name: 'Blog' }))
expect(generatedFiles.length).toEqual(unlinkSpy.mock.calls.length)
generatedFiles.forEach((f) => expect(unlinkSpy).toHaveBeenCalledWith(f))
})
})
13 changes: 13 additions & 0 deletions packages/cli/src/commands/destroy/layout/layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { files as layoutFiles } from '../../generate/layout/layout'
import { createYargsForComponentDestroy } from '../helpers'

export const {
command,
desc,
builder,
handler,
tasks,
} = createYargsForComponentDestroy({
componentName: 'layout',
filesFn: layoutFiles,
})
80 changes: 80 additions & 0 deletions packages/cli/src/commands/destroy/page/__tests__/page.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
global.__dirname = __dirname
jest.mock('fs')
jest.mock('src/lib', () => {
return {
...require.requireActual('src/lib'),
generateTemplate: () => '',
}
})

import fs from 'fs'

import 'src/lib/test'
import { getPaths } from 'src/lib'

import { files } from '../../../generate/page/page'
import { tasks } from '../page'

beforeEach(() => {
fs.__setMockFiles({
...files({ name: 'About' }),
[getPaths().web.routes]: [
'<Routes>',
' <Route path="/about" page={AboutPage} name="about" />',
' <Route path="/" page={HomePage} name="home" />',
' <Route notfound page={NotFoundPage} />',
'</Routes>',
].join('\n'),
})
})

afterEach(() => {
fs.__setMockFiles({})
jest.spyOn(fs, 'unlinkSync').mockClear()
})

test('destroys page files', async () => {
const unlinkSpy = jest.spyOn(fs, 'unlinkSync')
const t = tasks({ name: 'About' })
t.setRenderer('silent')

return t._tasks[0].run().then(() => {
cannikin marked this conversation as resolved.
Show resolved Hide resolved
const generatedFiles = Object.keys(files({ name: 'About' }))
expect(generatedFiles.length).toEqual(unlinkSpy.mock.calls.length)
generatedFiles.forEach((f) => expect(unlinkSpy).toHaveBeenCalledWith(f))
})
})

test('cleans up route from Routes.js', async () => {
const t = tasks({ name: 'About' })
t.setRenderer('silent')

return t._tasks[1].run().then(() => {
const routes = fs.readFileSync(getPaths().web.routes)
expect(routes).toEqual(
[
'<Routes>',
' <Route path="/" page={HomePage} name="home" />',
' <Route notfound page={NotFoundPage} />',
'</Routes>',
].join('\n')
)
})
})

test('cleans up route with a custom path from Routes.js', async () => {
const t = tasks({ name: 'About', path: '/about-us' })
t.setRenderer('silent')

return t._tasks[1].run().then(() => {
const routes = fs.readFileSync(getPaths().web.routes)
expect(routes).toEqual(
[
'<Routes>',
' <Route path="/" page={HomePage} name="home" />',
' <Route notfound page={NotFoundPage} />',
'</Routes>',
].join('\n')
)
})
})
Loading