added tests for generated template files

Summary:
Created a test that snapshots the generated files, so that we can capture accidental regressions when generating files.

Also made the package id to package name a bit more robust

Reviewed By: jknoxville

Differential Revision: D21619633

fbshipit-source-id: 88ffb127e050d840df9ccd4b15ba29a71f341975
This commit is contained in:
Michel Weststrate
2020-05-19 05:31:05 -07:00
committed by Facebook GitHub Bot
parent 3f86c9f6d2
commit 49b4022228
4 changed files with 187 additions and 36 deletions

View File

@@ -47,7 +47,8 @@
"postpack": "rimraf oclif.manifest.json", "postpack": "rimraf oclif.manifest.json",
"prepack": "yarn reset && yarn build && oclif-dev manifest && oclif-dev readme", "prepack": "yarn reset && yarn build && oclif-dev manifest && oclif-dev readme",
"run": "yarn build && bin/run", "run": "yarn build && bin/run",
"version": "oclif-dev readme && hg add README.md" "version": "oclif-dev readme && hg add README.md",
"test": "yarn jest"
}, },
"engines": { "engines": {
"node": ">=8.0.0" "node": ">=8.0.0"

View File

@@ -0,0 +1,141 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
import fse from 'fs-extra';
import {initTemplate} from '../commands/init';
let files: Record<string, string> = {};
beforeEach(() => {
function ensureDir() {
// no implementation
}
function writeFile(name: string, contents: string) {
files[name] = contents;
}
files = {};
jest.mock('fs-extra', () => jest.fn());
// @ts-ignore
fse.ensureDir = ensureDir;
// @ts-ignore
fse.writeFile = writeFile;
});
afterEach(() => {
// @ts-ignore
// fse.ensureDir.mockRestore();
// @ts-ignore
// fs.writeFile.mockRestore();
});
test('It generates the correct files', async () => {
await initTemplate('my weird Package %name. etc', 'Nice title', '/dev/null');
expect(files).toMatchInlineSnapshot(`
Object {
"/dev/null/.gitignore": "node_modules
dist/
",
"/dev/null/package.json": "{
\\"$schema\\": \\"https://fbflipper.com/schemas/plugin-package/v2.json\\",
\\"name\\": \\"flipper-plugin-my-weird-package-name-etc\\",
\\"id\\": \\"my weird Package %name. etc\\",
\\"version\\": \\"1.0.0\\",
\\"main\\": \\"dist/bundle.js\\",
\\"flipperBundlerEntry\\": \\"src/index.tsx\\",
\\"license\\": \\"MIT\\",
\\"keywords\\": [
\\"flipper-plugin\\"
],
\\"icon\\": \\"apps\\",
\\"title\\": \\"Nice title\\",
\\"scripts\\": {
\\"lint\\": \\"flipper-pkg lint\\",
\\"prepack\\": \\"flipper-pkg lint && flipper-pkg bundle\\",
\\"build\\": \\"flipper-pkg bundle\\",
\\"watch\\": \\"flipper-pkg bundle --watch\\"
},
\\"peerDependencies\\": {
\\"flipper\\": \\"latest\\"
},
\\"devDependencies\\": {
\\"@types/react\\": \\"latest\\",
\\"@types/react-dom\\": \\"latest\\",
\\"flipper\\": \\"latest\\",
\\"flipper-pkg\\": \\"latest\\"
}
}
",
"/dev/null/src/index.tsx": "import React from 'react';
import {FlipperPlugin, View, KeyboardActions} from 'flipper';
type State = {};
type Data = {};
type PersistedState = {
data: Array<Data>;
};
export default class extends FlipperPlugin<State, any, PersistedState> {
static keyboardActions: KeyboardActions = ['clear'];
static defaultPersistedState: PersistedState = {
data: [],
};
static persistedStateReducer = (
persistedState: PersistedState,
method: string,
data: Data,
): PersistedState => {
return {
...persistedState,
data: persistedState.data.concat([data]),
};
};
state = {};
onKeyboardAction = (action: string) => {
if (action === 'clear') {
this.props.setPersistedState({data: []});
}
};
render() {
return (
<View scrollable>
{this.props.persistedState.data.map((d) => (
<div>{JSON.stringify(d, null, 2)}<hr/></div>
))}
</View>
)
}
}
",
"/dev/null/tsconfig.json": "{
\\"compilerOptions\\": {
\\"target\\": \\"ES2017\\",
\\"module\\": \\"ES6\\",
\\"jsx\\": \\"react\\",
\\"sourceMap\\": true,
\\"noEmit\\": true,
\\"strict\\": true,
\\"moduleResolution\\": \\"node\\",
\\"esModuleInterop\\": true,
\\"forceConsistentCasingInFileNames\\": true
},
\\"files\\": [\\"src/index.tsx\\"]
}
",
}
`);
});

View File

@@ -56,52 +56,61 @@ export default class Init extends Command {
}, },
]; ];
const pluginDirectory: string = path.resolve(process.cwd(), args.directory); const pluginDirectory: string = path.resolve(process.cwd(), args.directory);
const title: string = (await inquirer.prompt(titleQuestion)).title; const title: string = (await inquirer.prompt(titleQuestion)).title;
const packageNameSuffix = id.toLowerCase().replace(' ', '-'); const packageName = getPackageNameFromId(id);
const templateItems = await recursiveReaddir(templateDir); const outputDirectory = path.join(pluginDirectory, packageName);
const outputDirectory = path.join(
pluginDirectory,
'flipper-plugin-' + packageNameSuffix,
);
if (fs.existsSync(outputDirectory)) { if (fs.existsSync(outputDirectory)) {
console.error(`Directory '${outputDirectory}' already exists`); console.error(`Directory '${outputDirectory}' already exists`);
process.exit(1); process.exit(1);
} }
await fs.ensureDir(outputDirectory);
console.log( console.log(
`⚙️ Initializing Flipper desktop template in ${outputDirectory}`, `⚙️ Initializing Flipper desktop template in ${outputDirectory}`,
); );
await fs.ensureDir(outputDirectory);
initTemplate(id, title, outputDirectory);
for (const item of templateItems) { console.log(`⚙️ Installing dependencies`);
const lstat = await fs.lstat(item);
if (lstat.isFile()) {
const file = path.relative(templateDir, item);
const dir = path.dirname(file);
const newDir = path.join(outputDirectory, dir);
const newFile = file.endsWith('.template')
? path.join(
outputDirectory,
file.substring(0, file.length - templateExt.length),
)
: path.join(outputDirectory, file);
await fs.ensureDir(newDir);
const content = (await fs.readFile(item))
.toString()
.replace('{{id}}', id)
.replace('{{title}}', title)
.replace('{{package_name_suffix}}', packageNameSuffix);
await fs.writeFile(newFile, content);
}
}
spawnSync('yarn', ['install'], {cwd: outputDirectory, stdio: [0, 1, 2]}); spawnSync('yarn', ['install'], {cwd: outputDirectory, stdio: [0, 1, 2]});
console.log( console.log(
`✅ Plugin directory initialized. Package name: flipper-plugin-${packageNameSuffix}.`, `✅ Plugin directory initialized. Package name: ${packageName}.`,
);
console.log(
` Run 'cd flipper-plugin-${packageNameSuffix} && yarn watch' to get started!`,
); );
console.log(` Run 'cd ${packageName} && yarn watch' to get started!`);
}
}
function getPackageNameFromId(id: string): string {
return 'flipper-plugin-' + id.toLowerCase().replace(/[^a-zA-Z0-9\-_]+/g, '-');
}
export async function initTemplate(
id: string,
title: string,
outputDirectory: string,
) {
const packageName = getPackageNameFromId(id);
const templateItems = await recursiveReaddir(templateDir);
for (const item of templateItems) {
const lstat = await fs.lstat(item);
if (lstat.isFile()) {
const file = path.relative(templateDir, item);
const dir = path.dirname(file);
const newDir = path.join(outputDirectory, dir);
const newFile = file.endsWith(templateExt)
? path.join(
outputDirectory,
file.substring(0, file.length - templateExt.length),
)
: path.join(outputDirectory, file);
await fs.ensureDir(newDir);
const content = (await fs.readFile(item))
.toString()
.replace('{{id}}', id)
.replace('{{title}}', title)
.replace('{{package_name}}', packageName);
await fs.writeFile(newFile, content);
}
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"$schema": "https://fbflipper.com/schemas/plugin-package/v2.json", "$schema": "https://fbflipper.com/schemas/plugin-package/v2.json",
"name": "flipper-plugin-{{package_name_suffix}}", "name": "{{package_name}}",
"id": "{{id}}", "id": "{{id}}",
"version": "1.0.0", "version": "1.0.0",
"main": "dist/bundle.js", "main": "dist/bundle.js",