├── .eslintignore
├── .gitignore
├── commitlint.config.js
├── bin
└── snap-it.js
├── .prettierrc
├── src
├── types
│ ├── getNumberType.ts
│ ├── getObjectType.ts
│ ├── getBooleanType.ts
│ └── getStringType.ts
├── utils
│ ├── functionUtils.ts
│ └── logger.ts
├── serializeSymbol.ts
├── getProps.ts
├── generateTestFile.ts
└── index.ts
├── .babelrc
├── lib
├── types
│ ├── getNumberType.js.map
│ ├── getObjectType.js.map
│ ├── getBooleanType.js.map
│ ├── getObjectType.js
│ ├── getNumberType.js
│ ├── getBooleanType.js
│ ├── getStringType.js
│ └── getStringType.js.map
├── utils
│ ├── functionUtils.js
│ ├── functionUtils.js.map
│ ├── logger.js
│ └── logger.js.map
├── serializeSymbol.js.map
├── serializeSymbol.js
├── generateTestFile.js
├── getProps.js
├── getProps.js.map
├── index.js
├── generateTestFile.js.map
└── index.js.map
├── .release-it.json
├── example
├── Search.tsx
└── Search.test.tsx
├── tsconfig.json
├── template
└── template
├── package.json
└── README.md
/.eslintignore:
--------------------------------------------------------------------------------
1 | template/*
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | __tests__
3 | yarn-error.log
4 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ['@commitlint/config-conventional'],
3 | };
4 |
--------------------------------------------------------------------------------
/bin/snap-it.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | // eslint-disable-next-line import/no-commonjs
4 | require('../lib/index');
5 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | 'singleQuote': true,
3 | 'tabWidth': 2,
4 | 'trailingComma': 'es5',
5 | 'useTabs': false
6 | }
7 |
--------------------------------------------------------------------------------
/src/types/getNumberType.ts:
--------------------------------------------------------------------------------
1 | function getNumberType(): string {
2 | return '123';
3 | }
4 |
5 | export default getNumberType;
6 |
--------------------------------------------------------------------------------
/src/types/getObjectType.ts:
--------------------------------------------------------------------------------
1 | function getObjectType(): string {
2 | return '{}';
3 | }
4 |
5 | export default getObjectType;
6 |
--------------------------------------------------------------------------------
/src/types/getBooleanType.ts:
--------------------------------------------------------------------------------
1 | function getBooleanType(): string {
2 | return 'true';
3 | }
4 |
5 | export default getBooleanType;
6 |
--------------------------------------------------------------------------------
/src/utils/functionUtils.ts:
--------------------------------------------------------------------------------
1 | export function isFunction(type: string) {
2 | const brackets = /\((.*)\)/;
3 | return !!brackets.test(type);
4 | }
5 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/preset-env", {
4 | "targets": {
5 | "node": "8"
6 | }
7 | }],
8 | "@babel/preset-typescript"
9 | ],
10 | "plugins": ["@babel/plugin-proposal-optional-chaining"]
11 | }
12 |
--------------------------------------------------------------------------------
/lib/types/getNumberType.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../../src/types/getNumberType.ts"],"names":["getNumberType"],"mappings":";;;;;;;AAAA,SAASA,aAAT,GAAiC;AAC/B,SAAO,KAAP;AACD;;eAEcA,a","sourcesContent":["function getNumberType(): string {\n return '123';\n}\n\nexport default getNumberType;\n"],"file":"getNumberType.js"}
--------------------------------------------------------------------------------
/lib/types/getObjectType.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../../src/types/getObjectType.ts"],"names":["getObjectType"],"mappings":";;;;;;;AAAA,SAASA,aAAT,GAAiC;AAC/B,SAAO,IAAP;AACD;;eAEcA,a","sourcesContent":["function getObjectType(): string {\n return '{}';\n}\n\nexport default getObjectType;\n"],"file":"getObjectType.js"}
--------------------------------------------------------------------------------
/lib/types/getBooleanType.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../../src/types/getBooleanType.ts"],"names":["getBooleanType"],"mappings":";;;;;;;AAAA,SAASA,cAAT,GAAkC;AAChC,SAAO,MAAP;AACD;;eAEcA,c","sourcesContent":["function getBooleanType(): string {\n return 'true';\n}\n\nexport default getBooleanType;\n"],"file":"getBooleanType.js"}
--------------------------------------------------------------------------------
/lib/utils/functionUtils.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.isFunction = isFunction;
7 |
8 | function isFunction(type) {
9 | const brackets = /\((.*)\)/;
10 | return !!brackets.test(type);
11 | }
12 | //# sourceMappingURL=functionUtils.js.map
--------------------------------------------------------------------------------
/lib/types/getObjectType.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = void 0;
7 |
8 | function getObjectType() {
9 | return '{}';
10 | }
11 |
12 | var _default = getObjectType;
13 | exports.default = _default;
14 | //# sourceMappingURL=getObjectType.js.map
--------------------------------------------------------------------------------
/lib/types/getNumberType.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = void 0;
7 |
8 | function getNumberType() {
9 | return '123';
10 | }
11 |
12 | var _default = getNumberType;
13 | exports.default = _default;
14 | //# sourceMappingURL=getNumberType.js.map
--------------------------------------------------------------------------------
/lib/types/getBooleanType.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = void 0;
7 |
8 | function getBooleanType() {
9 | return 'true';
10 | }
11 |
12 | var _default = getBooleanType;
13 | exports.default = _default;
14 | //# sourceMappingURL=getBooleanType.js.map
--------------------------------------------------------------------------------
/.release-it.json:
--------------------------------------------------------------------------------
1 | {
2 | "git": {
3 | "commitMessage": "chore: release ${version}",
4 | "tagName": "v${version}"
5 | },
6 | "npm": {
7 | "publish": true
8 | },
9 | "github": {
10 | "release": true
11 | },
12 | "plugins": {
13 | "@release-it/conventional-changelog": {
14 | "preset": "angular"
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/types/getStringType.ts:
--------------------------------------------------------------------------------
1 | function getStringType(name: string): string {
2 | // TODO: move checking color to separate function and make it maybe regex?
3 | switch (name) {
4 | case 'color':
5 | case 'backgroundColor':
6 | case 'bgColor':
7 | return '#ffffff';
8 | default:
9 | return 'testing string';
10 | }
11 | }
12 |
13 | export default getStringType;
14 |
--------------------------------------------------------------------------------
/src/utils/logger.ts:
--------------------------------------------------------------------------------
1 | import chalk from 'chalk';
2 |
3 | const logger = (type: string, color: Function) => (...messages: unknown[]) => {
4 | console.log(chalk.bgGray(chalk.white(type)), color(...messages));
5 | };
6 |
7 | export const info = logger('ℹ', chalk.white);
8 | export const warn = logger('⚠', chalk.yellow);
9 | export const error = logger('✖', chalk.red);
10 | export const success = logger('✓', chalk.green);
11 |
--------------------------------------------------------------------------------
/lib/utils/functionUtils.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../../src/utils/functionUtils.ts"],"names":["isFunction","type","brackets","test"],"mappings":";;;;;;;AAAO,SAASA,UAAT,CAAoBC,IAApB,EAAkC;AACvC,QAAMC,QAAQ,GAAG,UAAjB;AACA,SAAO,CAAC,CAACA,QAAQ,CAACC,IAAT,CAAcF,IAAd,CAAT;AACD","sourcesContent":["export function isFunction(type: string) {\n const brackets = /\\((.*)\\)/;\n return !!brackets.test(type);\n}\n"],"file":"functionUtils.js"}
--------------------------------------------------------------------------------
/example/Search.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View } from 'react-native';
3 |
4 | interface Props {
5 | numberTest?: number;
6 | booleanTest?: boolean;
7 | stringTest?: string;
8 | anyTest?: any;
9 | functionTest?: () => void;
10 | color?: string;
11 | backgroundColor?: string;
12 | bgColor?: string;
13 |
14 | requiredTest: string;
15 | }
16 |
17 | export default function Search({}: Props) {
18 | return ;
19 | }
20 |
--------------------------------------------------------------------------------
/lib/types/getStringType.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.default = void 0;
7 |
8 | function getStringType(name) {
9 | // TODO: move checking color to separate function and make it maybe regex?
10 | switch (name) {
11 | case 'color':
12 | case 'backgroundColor':
13 | case 'bgColor':
14 | return '#ffffff';
15 |
16 | default:
17 | return 'testing string';
18 | }
19 | }
20 |
21 | var _default = getStringType;
22 | exports.default = _default;
23 | //# sourceMappingURL=getStringType.js.map
--------------------------------------------------------------------------------
/lib/types/getStringType.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../../src/types/getStringType.ts"],"names":["getStringType","name"],"mappings":";;;;;;;AAAA,SAASA,aAAT,CAAuBC,IAAvB,EAA6C;AAC3C;AACA,UAAQA,IAAR;AACE,SAAK,OAAL;AACA,SAAK,iBAAL;AACA,SAAK,SAAL;AACE,aAAO,SAAP;;AACF;AACE,aAAO,gBAAP;AANJ;AAQD;;eAEcD,a","sourcesContent":["function getStringType(name: string): string {\n // TODO: move checking color to separate function and make it maybe regex?\n switch (name) {\n case 'color':\n case 'backgroundColor':\n case 'bgColor':\n return '#ffffff';\n default:\n return 'testing string';\n }\n}\n\nexport default getStringType;\n"],"file":"getStringType.js"}
--------------------------------------------------------------------------------
/src/serializeSymbol.ts:
--------------------------------------------------------------------------------
1 | import * as ts from 'typescript';
2 |
3 | export interface TestPropsInfo {
4 | name: string;
5 | documentation: string;
6 | type: string;
7 | required: boolean;
8 | }
9 |
10 | export function serializeSymbol(
11 | symbol: ts.Symbol,
12 | checker: ts.TypeChecker
13 | ): TestPropsInfo {
14 | return {
15 | name: symbol.getName(),
16 | documentation: ts.displayPartsToString(
17 | symbol.getDocumentationComment(checker)
18 | ),
19 | type: checker.typeToString(
20 | checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration!)
21 | ),
22 | // @ts-ignore
23 | required: !symbol.valueDeclaration.questionToken,
24 | };
25 | }
26 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowUnreachableCode": false,
4 | "allowUnusedLabels": false,
5 | "esModuleInterop": true,
6 | "forceConsistentCasingInFileNames": true,
7 | "jsx": "react",
8 | "lib": ["esnext"],
9 | "module": "esnext",
10 | "moduleResolution": "node",
11 | "noFallthroughCasesInSwitch": true,
12 | "noImplicitReturns": true,
13 | "noImplicitThis": true,
14 | "noUnusedLocals": true,
15 | "noUnusedParameters": true,
16 | "resolveJsonModule": true,
17 | "skipLibCheck": true,
18 | "target": "esnext",
19 | "plugins": [{ "name": "typescript-tslint-plugin" }],
20 | },
21 | "exclude": ["template/**/*"]
22 | }
23 |
--------------------------------------------------------------------------------
/lib/utils/logger.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.success = exports.error = exports.warn = exports.info = void 0;
7 |
8 | var _chalk = _interopRequireDefault(require("chalk"));
9 |
10 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11 |
12 | const logger = (type, color) => (...messages) => {
13 | console.log(_chalk.default.bgGray(_chalk.default.white(type)), color(...messages));
14 | };
15 |
16 | const info = logger('ℹ', _chalk.default.white);
17 | exports.info = info;
18 | const warn = logger('⚠', _chalk.default.yellow);
19 | exports.warn = warn;
20 | const error = logger('✖', _chalk.default.red);
21 | exports.error = error;
22 | const success = logger('✓', _chalk.default.green);
23 | exports.success = success;
24 | //# sourceMappingURL=logger.js.map
--------------------------------------------------------------------------------
/template/template:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from '@testing-library/react-native';
3 |
4 | import { <%= componentName %> } from '<%= filepath %>';
5 |
6 | describe('<%= componentName %>', () => {
7 | test('Snaphot for required props', () => {
8 | const props = {
9 | <%- getRequiredProps(data) %>
10 | }
11 | const tree = render(<<%= componentName %> {...props} />).toJSON();
12 | expect(tree).toMatchSnapshot();
13 | });
14 | <% for(var i=0; i < getOptionalPropsArray(data).length; i++) {%>
15 | test('Snaphot for <%= getOptionalPropsArray(data)[i].name %>', () => {
16 | const props = {
17 | <%- getRequiredProps(data) %>
18 | <%= getOptionalPropsArray(data)[i].name %>: <%- getPropValue(getOptionalPropsArray(data)[i]) %>,
19 | }
20 | const tree = render(<<%= componentName %> {...props} />).toJSON();
21 | expect(tree).toMatchSnapshot();
22 | });
23 | <% } %>
24 | });
25 |
--------------------------------------------------------------------------------
/lib/utils/logger.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../../src/utils/logger.ts"],"names":["logger","type","color","messages","console","log","chalk","bgGray","white","info","warn","yellow","error","red","success","green"],"mappings":";;;;;;;AAAA;;;;AAEA,MAAMA,MAAM,GAAG,CAACC,IAAD,EAAeC,KAAf,KAAmC,CAAC,GAAGC,QAAJ,KAA4B;AAC5EC,EAAAA,OAAO,CAACC,GAAR,CAAYC,eAAMC,MAAN,CAAaD,eAAME,KAAN,CAAYP,IAAZ,CAAb,CAAZ,EAA6CC,KAAK,CAAC,GAAGC,QAAJ,CAAlD;AACD,CAFD;;AAIO,MAAMM,IAAI,GAAGT,MAAM,CAAC,GAAD,EAAMM,eAAME,KAAZ,CAAnB;;AACA,MAAME,IAAI,GAAGV,MAAM,CAAC,GAAD,EAAMM,eAAMK,MAAZ,CAAnB;;AACA,MAAMC,KAAK,GAAGZ,MAAM,CAAC,GAAD,EAAMM,eAAMO,GAAZ,CAApB;;AACA,MAAMC,OAAO,GAAGd,MAAM,CAAC,GAAD,EAAMM,eAAMS,KAAZ,CAAtB","sourcesContent":["import chalk from 'chalk';\n\nconst logger = (type: string, color: Function) => (...messages: unknown[]) => {\n console.log(chalk.bgGray(chalk.white(type)), color(...messages));\n};\n\nexport const info = logger('ℹ', chalk.white);\nexport const warn = logger('⚠', chalk.yellow);\nexport const error = logger('✖', chalk.red);\nexport const success = logger('✓', chalk.green);\n"],"file":"logger.js"}
--------------------------------------------------------------------------------
/lib/serializeSymbol.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../src/serializeSymbol.ts"],"names":["serializeSymbol","symbol","checker","name","getName","documentation","ts","displayPartsToString","getDocumentationComment","type","typeToString","getTypeOfSymbolAtLocation","valueDeclaration","required","questionToken"],"mappings":";;;;;;;AAAA;;;;;;AASO,SAASA,eAAT,CACLC,MADK,EAELC,OAFK,EAGU;AACf,SAAO;AACLC,IAAAA,IAAI,EAAEF,MAAM,CAACG,OAAP,EADD;AAELC,IAAAA,aAAa,EAAEC,EAAE,CAACC,oBAAH,CACbN,MAAM,CAACO,uBAAP,CAA+BN,OAA/B,CADa,CAFV;AAKLO,IAAAA,IAAI,EAAEP,OAAO,CAACQ,YAAR,CACJR,OAAO,CAACS,yBAAR,CAAkCV,MAAlC,EAA0CA,MAAM,CAACW,gBAAjD,CADI,CALD;AAQL;AACAC,IAAAA,QAAQ,EAAE,CAACZ,MAAM,CAACW,gBAAP,CAAwBE;AAT9B,GAAP;AAWD","sourcesContent":["import * as ts from 'typescript';\n\nexport interface TestPropsInfo {\n name: string;\n documentation: string;\n type: string;\n required: boolean;\n}\n\nexport function serializeSymbol(\n symbol: ts.Symbol,\n checker: ts.TypeChecker\n): TestPropsInfo {\n return {\n name: symbol.getName(),\n documentation: ts.displayPartsToString(\n symbol.getDocumentationComment(checker)\n ),\n type: checker.typeToString(\n checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration!)\n ),\n // @ts-ignore\n required: !symbol.valueDeclaration.questionToken,\n };\n}\n"],"file":"serializeSymbol.js"}
--------------------------------------------------------------------------------
/src/getProps.ts:
--------------------------------------------------------------------------------
1 | import { isFunction } from './utils/functionUtils';
2 | import getStringType from './types/getStringType';
3 | import getNumberType from './types/getNumberType';
4 | import getBooleanType from './types/getBooleanType';
5 | import getObjectType from './types/getObjectType';
6 |
7 | export function getRequiredProps(data) {
8 | let requriedString = '';
9 | for (let i = 0; i < data.length; i++) {
10 | if (data[i].required) {
11 | requriedString += `${data[i].name}: ${getPropValue(data[i])},\n`;
12 | }
13 | }
14 | return requriedString;
15 | }
16 |
17 | // TODO: move generating this data to be similar to getRequiredProps so we can get rid of implementation inside template (template/template)
18 | export function getOptionalPropsArray(data) {
19 | return data.filter((d) => !d.required);
20 | }
21 |
22 | export function getPropValue(data) {
23 | const type = data.type;
24 |
25 | if (isFunction(type)) {
26 | return 'jest.fn()';
27 | }
28 | // TODO: move all of this to separate functions like getStringType
29 | switch (type) {
30 | case 'string':
31 | return `'${getStringType(data.name)}'`;
32 | case 'number':
33 | return `'${getNumberType()}'`;
34 | case 'boolean':
35 | return `'${getBooleanType()}'`;
36 | case 'object':
37 | return `'${getObjectType()}'`;
38 | case 'any':
39 | return `'${getObjectType()}'`;
40 | default:
41 | return 'undefined';
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/generateTestFile.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import ejs from 'ejs';
3 | import fs from 'fs-extra';
4 |
5 | import { info, success } from './utils/logger';
6 | import { TestPropsInfo } from './serializeSymbol';
7 | import {
8 | getRequiredProps,
9 | getOptionalPropsArray,
10 | getPropValue,
11 | } from './getProps';
12 |
13 | const TEMPLATE = path.resolve(__dirname, '../template/template');
14 |
15 | export async function generateTestFile(
16 | data: TestPropsInfo[],
17 | filename: string,
18 | filepath: string,
19 | options: { saveToSameFolder: boolean }
20 | ) {
21 | const root = process.cwd();
22 |
23 | let dest = path.relative(root, '__tests__');
24 | if (options.saveToSameFolder) {
25 | console.log(filepath);
26 | dest = path.relative(root, filepath.match(/(.*)[\/\\]/)[1] || '');
27 | }
28 | const filepathRelative = options.saveToSameFolder
29 | ? `./${path.relative(dest, filepath)}`
30 | : path.relative(dest, filepath);
31 | await fs.mkdirp(dest);
32 |
33 | const target = `${dest}/${filename}.test.tsx`;
34 | const content = await fs.readFile(TEMPLATE, 'utf8');
35 |
36 | info(`Writing to ${target}`);
37 | await fs.writeFile(
38 | target,
39 | ejs.render(content, {
40 | data,
41 | getRequiredProps,
42 | getOptionalPropsArray,
43 | getPropValue,
44 | componentName: filename,
45 | filepath: filepathRelative,
46 | })
47 | );
48 | success(`Done, check your test in ${target}`);
49 | }
50 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import * as ts from 'typescript';
3 | import yargs from 'yargs';
4 |
5 | import { info, error } from './utils/logger';
6 | import { serializeSymbol, TestPropsInfo } from './serializeSymbol';
7 | import { generateTestFile } from './generateTestFile';
8 |
9 | yargs.command('g ', 'create a snapshot for component', {}, snapshot).argv;
10 |
11 | async function snapshot(argv: yargs.Arguments) {
12 | const saveToSameFolder = argv.direct === 'true';
13 |
14 | const root = process.cwd();
15 | const fileName = path.relative(root, argv.name);
16 | info(`Reading: ${fileName}`);
17 |
18 | const testNameRegex = /[^\/]+(?=\.)/g;
19 | const testName = fileName.match(testNameRegex)[0];
20 |
21 | const program = ts.createProgram([fileName], {});
22 | const checker = program.getTypeChecker();
23 | const source = program.getSourceFile(fileName);
24 |
25 | if (!source) {
26 | error('Typescript source file is required');
27 | return;
28 | }
29 |
30 | ts.forEachChild(source, (node) => {
31 | // TODO: get also function and class, extract data from there like e.g. default values
32 | if (ts.isInterfaceDeclaration(node)) {
33 | const symbol = checker.getSymbolAtLocation(node.name);
34 | const data = [] as TestPropsInfo[];
35 | symbol.members.forEach((loc) => {
36 | const info = serializeSymbol(loc, checker);
37 | data.push(info);
38 | });
39 | generateTestFile(data, testName, fileName, { saveToSameFolder });
40 | }
41 | });
42 | }
43 |
--------------------------------------------------------------------------------
/lib/serializeSymbol.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.serializeSymbol = serializeSymbol;
7 |
8 | var ts = _interopRequireWildcard(require("typescript"));
9 |
10 | function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
11 |
12 | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
13 |
14 | function serializeSymbol(symbol, checker) {
15 | return {
16 | name: symbol.getName(),
17 | documentation: ts.displayPartsToString(symbol.getDocumentationComment(checker)),
18 | type: checker.typeToString(checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration)),
19 | // @ts-ignore
20 | required: !symbol.valueDeclaration.questionToken
21 | };
22 | }
23 | //# sourceMappingURL=serializeSymbol.js.map
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@patys/snap-it",
3 | "version": "0.3.0",
4 | "description": "Tool to generate jest test cases with snapshots for React Native components.",
5 | "main": "index.ts",
6 | "author": "Patryk Szczygło",
7 | "license": "MIT",
8 | "bin": {
9 | "snap-it": "bin/snap-it.js"
10 | },
11 | "publishConfig": {
12 | "access": "public",
13 | "registry": "https://registry.npmjs.org/"
14 | },
15 | "scripts": {
16 | "build": "babel --extensions .ts,.tsx src --out-dir lib --ignore '**/__tests__/**' --source-maps --delete-dir-on-start",
17 | "start": "yarn build && node lib/index.js",
18 | "lint": "./node_modules/.bin/tslint --project ./tsconfig.json",
19 | "release": "release-it"
20 | },
21 | "devDependencies": {
22 | "@babel/cli": "^7.12.1",
23 | "@commitlint/cli": "^11.0.0",
24 | "@commitlint/config-conventional": "^11.0.0",
25 | "@release-it/conventional-changelog": "^2.0.0",
26 | "@types/jest": "^25.1.2",
27 | "@types/node": "^14.14.6",
28 | "husky": "^4.3.0",
29 | "prettier": "2.1.2",
30 | "release-it": "^14.2.1"
31 | },
32 | "dependencies": {
33 | "@babel/core": "^7.12.3",
34 | "@babel/plugin-proposal-class-properties": "^7.12.1",
35 | "@babel/preset-env": "^7.12.1",
36 | "@babel/preset-flow": "^7.12.1",
37 | "@babel/preset-react": "^7.12.5",
38 | "@babel/preset-typescript": "^7.12.1",
39 | "chalk": "^4.1.0",
40 | "ejs": "^3.1.5",
41 | "fs-extra": "^9.0.1",
42 | "ts-jest": "^26.4.3",
43 | "tsc-watch": "^4.2.9",
44 | "tslint": "^6.1.3",
45 | "typescript": "^4.0.5",
46 | "yargs": "^16.1.0"
47 | },
48 | "husky": {
49 | "hooks": {
50 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/lib/generateTestFile.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.generateTestFile = generateTestFile;
7 |
8 | var _path = _interopRequireDefault(require("path"));
9 |
10 | var _ejs = _interopRequireDefault(require("ejs"));
11 |
12 | var _fsExtra = _interopRequireDefault(require("fs-extra"));
13 |
14 | var _logger = require("./utils/logger");
15 |
16 | var _getProps = require("./getProps");
17 |
18 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
19 |
20 | const TEMPLATE = _path.default.resolve(__dirname, '../template/template');
21 |
22 | async function generateTestFile(data, filename, filepath, options) {
23 | const root = process.cwd();
24 |
25 | let dest = _path.default.relative(root, '__tests__');
26 |
27 | if (options.saveToSameFolder) {
28 | console.log(filepath);
29 | dest = _path.default.relative(root, filepath.match(/(.*)[\/\\]/)[1] || '');
30 | }
31 |
32 | const filepathRelative = options.saveToSameFolder ? `./${_path.default.relative(dest, filepath)}` : _path.default.relative(dest, filepath);
33 | await _fsExtra.default.mkdirp(dest);
34 | const target = `${dest}/${filename}.test.tsx`;
35 | const content = await _fsExtra.default.readFile(TEMPLATE, 'utf8');
36 | (0, _logger.info)(`Writing to ${target}`);
37 | await _fsExtra.default.writeFile(target, _ejs.default.render(content, {
38 | data,
39 | getRequiredProps: _getProps.getRequiredProps,
40 | getOptionalPropsArray: _getProps.getOptionalPropsArray,
41 | getPropValue: _getProps.getPropValue,
42 | componentName: filename,
43 | filepath: filepathRelative
44 | }));
45 | (0, _logger.success)(`Done, check your test in ${target}`);
46 | }
47 | //# sourceMappingURL=generateTestFile.js.map
--------------------------------------------------------------------------------
/lib/getProps.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports.getRequiredProps = getRequiredProps;
7 | exports.getOptionalPropsArray = getOptionalPropsArray;
8 | exports.getPropValue = getPropValue;
9 |
10 | var _functionUtils = require("./utils/functionUtils");
11 |
12 | var _getStringType = _interopRequireDefault(require("./types/getStringType"));
13 |
14 | var _getNumberType = _interopRequireDefault(require("./types/getNumberType"));
15 |
16 | var _getBooleanType = _interopRequireDefault(require("./types/getBooleanType"));
17 |
18 | var _getObjectType = _interopRequireDefault(require("./types/getObjectType"));
19 |
20 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
21 |
22 | function getRequiredProps(data) {
23 | let requriedString = '';
24 |
25 | for (let i = 0; i < data.length; i++) {
26 | if (data[i].required) {
27 | requriedString += `${data[i].name}: ${getPropValue(data[i])},\n`;
28 | }
29 | }
30 |
31 | return requriedString;
32 | } // TODO: move generating this data to be similar to getRequiredProps so we can get rid of implementation inside template (template/template)
33 |
34 |
35 | function getOptionalPropsArray(data) {
36 | return data.filter(d => !d.required);
37 | }
38 |
39 | function getPropValue(data) {
40 | const type = data.type;
41 |
42 | if ((0, _functionUtils.isFunction)(type)) {
43 | return 'jest.fn()';
44 | } // TODO: move all of this to separate functions like getStringType
45 |
46 |
47 | switch (type) {
48 | case 'string':
49 | return `'${(0, _getStringType.default)(data.name)}'`;
50 |
51 | case 'number':
52 | return `'${(0, _getNumberType.default)()}'`;
53 |
54 | case 'boolean':
55 | return `'${(0, _getBooleanType.default)()}'`;
56 |
57 | case 'object':
58 | return `'${(0, _getObjectType.default)()}'`;
59 |
60 | case 'any':
61 | return `'${(0, _getObjectType.default)()}'`;
62 |
63 | default:
64 | return 'undefined';
65 | }
66 | }
67 | //# sourceMappingURL=getProps.js.map
--------------------------------------------------------------------------------
/lib/getProps.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../src/getProps.ts"],"names":["getRequiredProps","data","requriedString","i","length","required","name","getPropValue","getOptionalPropsArray","filter","d","type"],"mappings":";;;;;;;;;AAAA;;AACA;;AACA;;AACA;;AACA;;;;AAEO,SAASA,gBAAT,CAA0BC,IAA1B,EAAgC;AACrC,MAAIC,cAAc,GAAG,EAArB;;AACA,OAAK,IAAIC,CAAC,GAAG,CAAb,EAAgBA,CAAC,GAAGF,IAAI,CAACG,MAAzB,EAAiCD,CAAC,EAAlC,EAAsC;AACpC,QAAIF,IAAI,CAACE,CAAD,CAAJ,CAAQE,QAAZ,EAAsB;AACpBH,MAAAA,cAAc,IAAK,GAAED,IAAI,CAACE,CAAD,CAAJ,CAAQG,IAAK,KAAIC,YAAY,CAACN,IAAI,CAACE,CAAD,CAAL,CAAU,KAA5D;AACD;AACF;;AACD,SAAOD,cAAP;AACD,C,CAED;;;AACO,SAASM,qBAAT,CAA+BP,IAA/B,EAAqC;AAC1C,SAAOA,IAAI,CAACQ,MAAL,CAAaC,CAAD,IAAO,CAACA,CAAC,CAACL,QAAtB,CAAP;AACD;;AAEM,SAASE,YAAT,CAAsBN,IAAtB,EAA4B;AACjC,QAAMU,IAAI,GAAGV,IAAI,CAACU,IAAlB;;AAEA,MAAI,+BAAWA,IAAX,CAAJ,EAAsB;AACpB,WAAO,WAAP;AACD,GALgC,CAMjC;;;AACA,UAAQA,IAAR;AACE,SAAK,QAAL;AACE,aAAQ,IAAG,4BAAcV,IAAI,CAACK,IAAnB,CAAyB,GAApC;;AACF,SAAK,QAAL;AACE,aAAQ,IAAG,6BAAgB,GAA3B;;AACF,SAAK,SAAL;AACE,aAAQ,IAAG,8BAAiB,GAA5B;;AACF,SAAK,QAAL;AACE,aAAQ,IAAG,6BAAgB,GAA3B;;AACF,SAAK,KAAL;AACE,aAAQ,IAAG,6BAAgB,GAA3B;;AACF;AACE,aAAO,WAAP;AAZJ;AAcD","sourcesContent":["import { isFunction } from './utils/functionUtils';\nimport getStringType from './types/getStringType';\nimport getNumberType from './types/getNumberType';\nimport getBooleanType from './types/getBooleanType';\nimport getObjectType from './types/getObjectType';\n\nexport function getRequiredProps(data) {\n let requriedString = '';\n for (let i = 0; i < data.length; i++) {\n if (data[i].required) {\n requriedString += `${data[i].name}: ${getPropValue(data[i])},\\n`;\n }\n }\n return requriedString;\n}\n\n// TODO: move generating this data to be similar to getRequiredProps so we can get rid of implementation inside template (template/template)\nexport function getOptionalPropsArray(data) {\n return data.filter((d) => !d.required);\n}\n\nexport function getPropValue(data) {\n const type = data.type;\n\n if (isFunction(type)) {\n return 'jest.fn()';\n }\n // TODO: move all of this to separate functions like getStringType\n switch (type) {\n case 'string':\n return `'${getStringType(data.name)}'`;\n case 'number':\n return `'${getNumberType()}'`;\n case 'boolean':\n return `'${getBooleanType()}'`;\n case 'object':\n return `'${getObjectType()}'`;\n case 'any':\n return `'${getObjectType()}'`;\n default:\n return 'undefined';\n }\n}\n"],"file":"getProps.js"}
--------------------------------------------------------------------------------
/example/Search.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from 'react-native-testing-library';
3 |
4 | import { Search } from './Search.tsx';
5 |
6 | describe('Search', () => {
7 | test('Snaphot for required props', () => {
8 | const props = {
9 | requiredTest: 'testing string',
10 |
11 | }
12 | const tree = render().toJSON();
13 | expect(tree).toMatchSnapshot();
14 | });
15 |
16 | test('Snaphot for numberTest', () => {
17 | const props = {
18 | requiredTest: 'testing string',
19 |
20 | numberTest: '123',
21 | }
22 | const tree = render().toJSON();
23 | expect(tree).toMatchSnapshot();
24 | });
25 |
26 | test('Snaphot for booleanTest', () => {
27 | const props = {
28 | requiredTest: 'testing string',
29 |
30 | booleanTest: 'true',
31 | }
32 | const tree = render().toJSON();
33 | expect(tree).toMatchSnapshot();
34 | });
35 |
36 | test('Snaphot for stringTest', () => {
37 | const props = {
38 | requiredTest: 'testing string',
39 |
40 | stringTest: 'testing string',
41 | }
42 | const tree = render().toJSON();
43 | expect(tree).toMatchSnapshot();
44 | });
45 |
46 | test('Snaphot for anyTest', () => {
47 | const props = {
48 | requiredTest: 'testing string',
49 |
50 | anyTest: '{}',
51 | }
52 | const tree = render().toJSON();
53 | expect(tree).toMatchSnapshot();
54 | });
55 |
56 | test('Snaphot for functionTest', () => {
57 | const props = {
58 | requiredTest: 'testing string',
59 |
60 | functionTest: jest.fn(),
61 | }
62 | const tree = render().toJSON();
63 | expect(tree).toMatchSnapshot();
64 | });
65 |
66 | test('Snaphot for color', () => {
67 | const props = {
68 | requiredTest: 'testing string',
69 |
70 | color: '#ffffff',
71 | }
72 | const tree = render().toJSON();
73 | expect(tree).toMatchSnapshot();
74 | });
75 |
76 | test('Snaphot for backgroundColor', () => {
77 | const props = {
78 | requiredTest: 'testing string',
79 |
80 | backgroundColor: '#ffffff',
81 | }
82 | const tree = render().toJSON();
83 | expect(tree).toMatchSnapshot();
84 | });
85 |
86 | test('Snaphot for bgColor', () => {
87 | const props = {
88 | requiredTest: 'testing string',
89 |
90 | bgColor: '#ffffff',
91 | }
92 | const tree = render().toJSON();
93 | expect(tree).toMatchSnapshot();
94 | });
95 |
96 | });
97 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var _path = _interopRequireDefault(require("path"));
4 |
5 | var ts = _interopRequireWildcard(require("typescript"));
6 |
7 | var _yargs = _interopRequireDefault(require("yargs"));
8 |
9 | var _logger = require("./utils/logger");
10 |
11 | var _serializeSymbol = require("./serializeSymbol");
12 |
13 | var _generateTestFile = require("./generateTestFile");
14 |
15 | function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
16 |
17 | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
18 |
19 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
20 |
21 | _yargs.default.command('g ', 'create a snapshot for component', {}, snapshot).argv;
22 |
23 | async function snapshot(argv) {
24 | const saveToSameFolder = argv.direct === 'true';
25 | const root = process.cwd();
26 |
27 | const fileName = _path.default.relative(root, argv.name);
28 |
29 | (0, _logger.info)(`Reading: ${fileName}`);
30 | const testNameRegex = /[^\/]+(?=\.)/g;
31 | const testName = fileName.match(testNameRegex)[0];
32 | const program = ts.createProgram([fileName], {});
33 | const checker = program.getTypeChecker();
34 | const source = program.getSourceFile(fileName);
35 |
36 | if (!source) {
37 | (0, _logger.error)('Typescript source file is required');
38 | return;
39 | }
40 |
41 | ts.forEachChild(source, node => {
42 | // TODO: get also function and class, extract data from there like e.g. default values
43 | if (ts.isInterfaceDeclaration(node)) {
44 | const symbol = checker.getSymbolAtLocation(node.name);
45 | const data = [];
46 | symbol.members.forEach(loc => {
47 | const info = (0, _serializeSymbol.serializeSymbol)(loc, checker);
48 | data.push(info);
49 | });
50 | (0, _generateTestFile.generateTestFile)(data, testName, fileName, {
51 | saveToSameFolder
52 | });
53 | }
54 | });
55 | }
56 | //# sourceMappingURL=index.js.map
--------------------------------------------------------------------------------
/lib/generateTestFile.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../src/generateTestFile.ts"],"names":["TEMPLATE","path","resolve","__dirname","generateTestFile","data","filename","filepath","options","root","process","cwd","dest","relative","saveToSameFolder","console","log","match","filepathRelative","fs","mkdirp","target","content","readFile","writeFile","ejs","render","getRequiredProps","getOptionalPropsArray","getPropValue","componentName"],"mappings":";;;;;;;AAAA;;AACA;;AACA;;AAEA;;AAEA;;;;AAMA,MAAMA,QAAQ,GAAGC,cAAKC,OAAL,CAAaC,SAAb,EAAwB,sBAAxB,CAAjB;;AAEO,eAAeC,gBAAf,CACLC,IADK,EAELC,QAFK,EAGLC,QAHK,EAILC,OAJK,EAKL;AACA,QAAMC,IAAI,GAAGC,OAAO,CAACC,GAAR,EAAb;;AAEA,MAAIC,IAAI,GAAGX,cAAKY,QAAL,CAAcJ,IAAd,EAAoB,WAApB,CAAX;;AACA,MAAID,OAAO,CAACM,gBAAZ,EAA8B;AAC5BC,IAAAA,OAAO,CAACC,GAAR,CAAYT,QAAZ;AACAK,IAAAA,IAAI,GAAGX,cAAKY,QAAL,CAAcJ,IAAd,EAAoBF,QAAQ,CAACU,KAAT,CAAe,YAAf,EAA6B,CAA7B,KAAmC,EAAvD,CAAP;AACD;;AACD,QAAMC,gBAAgB,GAAGV,OAAO,CAACM,gBAAR,GACpB,KAAIb,cAAKY,QAAL,CAAcD,IAAd,EAAoBL,QAApB,CAA8B,EADd,GAErBN,cAAKY,QAAL,CAAcD,IAAd,EAAoBL,QAApB,CAFJ;AAGA,QAAMY,iBAAGC,MAAH,CAAUR,IAAV,CAAN;AAEA,QAAMS,MAAM,GAAI,GAAET,IAAK,IAAGN,QAAS,WAAnC;AACA,QAAMgB,OAAO,GAAG,MAAMH,iBAAGI,QAAH,CAAYvB,QAAZ,EAAsB,MAAtB,CAAtB;AAEA,oBAAM,cAAaqB,MAAO,EAA1B;AACA,QAAMF,iBAAGK,SAAH,CACJH,MADI,EAEJI,aAAIC,MAAJ,CAAWJ,OAAX,EAAoB;AAClBjB,IAAAA,IADkB;AAElBsB,IAAAA,gBAAgB,EAAhBA,0BAFkB;AAGlBC,IAAAA,qBAAqB,EAArBA,+BAHkB;AAIlBC,IAAAA,YAAY,EAAZA,sBAJkB;AAKlBC,IAAAA,aAAa,EAAExB,QALG;AAMlBC,IAAAA,QAAQ,EAAEW;AANQ,GAApB,CAFI,CAAN;AAWA,uBAAS,4BAA2BG,MAAO,EAA3C;AACD","sourcesContent":["import path from 'path';\nimport ejs from 'ejs';\nimport fs from 'fs-extra';\n\nimport { info, success } from './utils/logger';\nimport { TestPropsInfo } from './serializeSymbol';\nimport {\n getRequiredProps,\n getOptionalPropsArray,\n getPropValue,\n} from './getProps';\n\nconst TEMPLATE = path.resolve(__dirname, '../template/template');\n\nexport async function generateTestFile(\n data: TestPropsInfo[],\n filename: string,\n filepath: string,\n options: { saveToSameFolder: boolean }\n) {\n const root = process.cwd();\n\n let dest = path.relative(root, '__tests__');\n if (options.saveToSameFolder) {\n console.log(filepath);\n dest = path.relative(root, filepath.match(/(.*)[\\/\\\\]/)[1] || '');\n }\n const filepathRelative = options.saveToSameFolder\n ? `./${path.relative(dest, filepath)}`\n : path.relative(dest, filepath);\n await fs.mkdirp(dest);\n\n const target = `${dest}/${filename}.test.tsx`;\n const content = await fs.readFile(TEMPLATE, 'utf8');\n\n info(`Writing to ${target}`);\n await fs.writeFile(\n target,\n ejs.render(content, {\n data,\n getRequiredProps,\n getOptionalPropsArray,\n getPropValue,\n componentName: filename,\n filepath: filepathRelative,\n })\n );\n success(`Done, check your test in ${target}`);\n}\n"],"file":"generateTestFile.js"}
--------------------------------------------------------------------------------
/lib/index.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["../src/index.ts"],"names":["yargs","command","snapshot","argv","saveToSameFolder","direct","root","process","cwd","fileName","path","relative","name","testNameRegex","testName","match","program","ts","createProgram","checker","getTypeChecker","source","getSourceFile","forEachChild","node","isInterfaceDeclaration","symbol","getSymbolAtLocation","data","members","forEach","loc","info","push"],"mappings":";;AAAA;;AACA;;AACA;;AAEA;;AACA;;AACA;;;;;;;;AAEAA,eAAMC,OAAN,CAAc,UAAd,EAA0B,iCAA1B,EAA6D,EAA7D,EAAiEC,QAAjE,EAA2EC,IAA3E;;AAEA,eAAeD,QAAf,CAAwBC,IAAxB,EAAoD;AAClD,QAAMC,gBAAgB,GAAGD,IAAI,CAACE,MAAL,KAAgB,MAAzC;AAEA,QAAMC,IAAI,GAAGC,OAAO,CAACC,GAAR,EAAb;;AACA,QAAMC,QAAQ,GAAGC,cAAKC,QAAL,CAAcL,IAAd,EAAoBH,IAAI,CAACS,IAAzB,CAAjB;;AACA,oBAAM,YAAWH,QAAS,EAA1B;AAEA,QAAMI,aAAa,GAAG,eAAtB;AACA,QAAMC,QAAQ,GAAGL,QAAQ,CAACM,KAAT,CAAeF,aAAf,EAA8B,CAA9B,CAAjB;AAEA,QAAMG,OAAO,GAAGC,EAAE,CAACC,aAAH,CAAiB,CAACT,QAAD,CAAjB,EAA6B,EAA7B,CAAhB;AACA,QAAMU,OAAO,GAAGH,OAAO,CAACI,cAAR,EAAhB;AACA,QAAMC,MAAM,GAAGL,OAAO,CAACM,aAAR,CAAsBb,QAAtB,CAAf;;AAEA,MAAI,CAACY,MAAL,EAAa;AACX,uBAAM,oCAAN;AACA;AACD;;AAEDJ,EAAAA,EAAE,CAACM,YAAH,CAAgBF,MAAhB,EAAyBG,IAAD,IAAU;AAChC;AACA,QAAIP,EAAE,CAACQ,sBAAH,CAA0BD,IAA1B,CAAJ,EAAqC;AACnC,YAAME,MAAM,GAAGP,OAAO,CAACQ,mBAAR,CAA4BH,IAAI,CAACZ,IAAjC,CAAf;AACA,YAAMgB,IAAI,GAAG,EAAb;AACAF,MAAAA,MAAM,CAACG,OAAP,CAAeC,OAAf,CAAwBC,GAAD,IAAS;AAC9B,cAAMC,IAAI,GAAG,sCAAgBD,GAAhB,EAAqBZ,OAArB,CAAb;AACAS,QAAAA,IAAI,CAACK,IAAL,CAAUD,IAAV;AACD,OAHD;AAIA,8CAAiBJ,IAAjB,EAAuBd,QAAvB,EAAiCL,QAAjC,EAA2C;AAAEL,QAAAA;AAAF,OAA3C;AACD;AACF,GAXD;AAYD","sourcesContent":["import path from 'path';\nimport * as ts from 'typescript';\nimport yargs from 'yargs';\n\nimport { info, error } from './utils/logger';\nimport { serializeSymbol, TestPropsInfo } from './serializeSymbol';\nimport { generateTestFile } from './generateTestFile';\n\nyargs.command('g ', 'create a snapshot for component', {}, snapshot).argv;\n\nasync function snapshot(argv: yargs.Arguments) {\n const saveToSameFolder = argv.direct === 'true';\n\n const root = process.cwd();\n const fileName = path.relative(root, argv.name);\n info(`Reading: ${fileName}`);\n\n const testNameRegex = /[^\\/]+(?=\\.)/g;\n const testName = fileName.match(testNameRegex)[0];\n\n const program = ts.createProgram([fileName], {});\n const checker = program.getTypeChecker();\n const source = program.getSourceFile(fileName);\n\n if (!source) {\n error('Typescript source file is required');\n return;\n }\n\n ts.forEachChild(source, (node) => {\n // TODO: get also function and class, extract data from there like e.g. default values\n if (ts.isInterfaceDeclaration(node)) {\n const symbol = checker.getSymbolAtLocation(node.name);\n const data = [] as TestPropsInfo[];\n symbol.members.forEach((loc) => {\n const info = serializeSymbol(loc, checker);\n data.push(info);\n });\n generateTestFile(data, testName, fileName, { saveToSameFolder });\n }\n });\n}\n"],"file":"index.js"}
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # snap-it
2 |
3 | [](https://badge.fury.io/js/%40patys%2Fsnap-it)
4 |
5 | This is a tool to create a snapshot for your component. You can use it to simply generate boilerplate file with all cases. Keep in mind that you should verify and add your own test cases.
6 |
7 | Works only with TypeScript for now.
8 |
9 | ## Usage:
10 |
11 | Follow instalation guide and then:
12 |
13 | ```bash
14 | yarn create-snapshot components/Search.tsx
15 | ```
16 |
17 | Or you can use it directly without installing:
18 |
19 | ```bash
20 | npx @patys/snap-it g components/Search.tsx
21 | ```
22 |
23 | To save in the same directory use option: --direct=true
24 |
25 | ```bash
26 | npx @patys/snap-it g components/Search.tsx --direct=true
27 | ```
28 |
29 | Effect with direct:
30 | components/Search.tsx
31 | components/Search.test.tsx
32 |
33 | Effect without direct:
34 | components/Search.tsx
35 | \_\_tests\_\_/Search.test.tsx
36 |
37 | ## Installation:
38 |
39 | ```bash
40 | yarn add --dev @patys/snap-it
41 | ```
42 |
43 | Add to your package.json
44 |
45 | ```bash
46 | {
47 | "scripts": {
48 | ...
49 | "create-snapshot": "yarn snap-it g "
50 | ...
51 | }
52 | }
53 | ```
54 |
55 | ## Example:
56 |
57 | Your component:
58 |
59 | ```javascript
60 | // example/Search.tsx
61 | import React from 'react';
62 | import { View } from 'react-native';
63 |
64 | interface Props {
65 | numberTest?: number;
66 | booleanTest?: boolean;
67 | stringTest?: string;
68 | anyTest?: any;
69 | functionTest?: () => void;
70 |
71 | requiredTest: string;
72 | }
73 |
74 | export default function Search({}: Props) {
75 | return ;
76 | }
77 | ```
78 |
79 | Effect:
80 |
81 | ```javascript
82 | import React from 'react';
83 | import { render } from '@testing-library/react-native';
84 |
85 | import { Search } from '../example/Search.tsx';
86 |
87 | describe('Search', () => {
88 | test('Snaphot for required props', () => {
89 | const props = {
90 | requiredTest: 'testing string',
91 | };
92 | const tree = render().toJSON();
93 | expect(tree).toMatchSnapshot();
94 | });
95 |
96 | test('Snaphot for numberTest', () => {
97 | const props = {
98 | requiredTest: 'testing string',
99 |
100 | numberTest: 123,
101 | };
102 | const tree = render().toJSON();
103 | expect(tree).toMatchSnapshot();
104 | });
105 |
106 | test('Snaphot for booleanTest', () => {
107 | const props = {
108 | requiredTest: 'testing string',
109 |
110 | booleanTest: true,
111 | };
112 | const tree = render().toJSON();
113 | expect(tree).toMatchSnapshot();
114 | });
115 |
116 | test('Snaphot for stringTest', () => {
117 | const props = {
118 | requiredTest: 'testing string',
119 |
120 | stringTest: 'testing string',
121 | };
122 | const tree = render().toJSON();
123 | expect(tree).toMatchSnapshot();
124 | });
125 |
126 | test('Snaphot for anyTest', () => {
127 | const props = {
128 | requiredTest: 'testing string',
129 |
130 | anyTest: {},
131 | };
132 | const tree = render().toJSON();
133 | expect(tree).toMatchSnapshot();
134 | });
135 |
136 | test('Snaphot for functionTest', () => {
137 | const props = {
138 | requiredTest: 'testing string',
139 |
140 | functionTest: jest.fn(),
141 | };
142 | const tree = render().toJSON();
143 | expect(tree).toMatchSnapshot();
144 | });
145 | });
146 | ```
147 |
--------------------------------------------------------------------------------