1 | const R = require('ramda')
|
2 | const fs = require('fs')
|
3 | const debug = require('debug')('spdt')
|
4 | const { getConfig, STORYBOOK_PORT } = require('./config')
|
5 |
|
6 | const saveFile = (fileName, content) => {
|
7 | fs.writeFile(fileName, content, (err) => {
|
8 | if (err) {
|
9 | return debug(`error: ${err}`)
|
10 | } else {
|
11 | return debug(`File was saved: ${fileName}`)
|
12 | }
|
13 | })
|
14 | }
|
15 |
|
16 | const generateImports = () => `const TIMEOUT = 5000
|
17 | `
|
18 |
|
19 | const generateDescribe = (componentName, fixtureName, itTests) => `
|
20 | describe(
|
21 | '${componentName} - fixture ${fixtureName}',
|
22 | () => {
|
23 | let page
|
24 | let iFrame
|
25 | beforeAll(async () => {
|
26 | // eslint-disable-next-line no-underscore-dangle
|
27 | page = await global.__BROWSER__.newPage()
|
28 | // prettier-ignore
|
29 | await page.goto('http://localhost:${STORYBOOK_PORT}/?selectedKind=${componentName}&selectedStory=${fixtureName}')
|
30 | const frames = await page.frames()
|
31 | iFrame = frames.find((f) => f.name() === 'storybook-preview-iframe')
|
32 | }, TIMEOUT)
|
33 |
|
34 | afterAll(async () => {
|
35 | await page.close()
|
36 | })
|
37 | ${itTests}
|
38 | },
|
39 | TIMEOUT,
|
40 | )
|
41 | `
|
42 |
|
43 | const testCheckSelectorObject = (strOrObj) => {
|
44 | let selector
|
45 | let length
|
46 | if (typeof strOrObj === 'string') {
|
47 | selector = strOrObj
|
48 | length = 1
|
49 | } else if (typeof strOrObj === 'object' && !Array.isArray(strOrObj)) {
|
50 | ;({ length, selector } = strOrObj)
|
51 | }
|
52 |
|
53 | if (!selector || !length) {
|
54 | return null
|
55 | }
|
56 | return `
|
57 | it('should find component matching selector [${selector}] ${length} time(s)', async () => {
|
58 | const components = await iFrame.$$('${selector}')
|
59 | const expected = ${length}
|
60 | expect(components).toHaveLength(expected)
|
61 | })`
|
62 | }
|
63 |
|
64 | const testCheckSelector = (fixture) => {
|
65 | const { checkSelector } = R.propOr({}, 'spdt', fixture)
|
66 | if (!checkSelector) {
|
67 | return null
|
68 | }
|
69 | if (typeof checkSelector === 'string') {
|
70 | return testCheckSelectorObject(checkSelector)
|
71 | }
|
72 | if (Array.isArray(checkSelector)) {
|
73 | return checkSelector.map(testCheckSelectorObject)
|
74 | } else if (typeof checkSelector === 'object') {
|
75 | return testCheckSelectorObject(checkSelector)
|
76 | }
|
77 | return null
|
78 | }
|
79 |
|
80 | const testCheckSvg = (fixture) => {
|
81 | const { checkSvg } = R.propOr({}, 'spdt', fixture)
|
82 | if (checkSvg !== true) {
|
83 | return null
|
84 | }
|
85 | return `
|
86 | it('should load component as <svg>', async () => {
|
87 | const component = await iFrame.$('svg')
|
88 | expect(component._remoteObject.description).toMatch('svg') // eslint-disable-line no-underscore-dangle
|
89 | })`
|
90 | }
|
91 |
|
92 | const testCheckAxes = (fixture) => {
|
93 | const { checkAxes } = R.propOr({}, 'spdt', fixture)
|
94 | if (!Number.isInteger(checkAxes)) {
|
95 | return null
|
96 | }
|
97 | return `
|
98 | it('should have ${checkAxes} axes', async () => {
|
99 | const axes = await iFrame.$$('g.axis')
|
100 | const expected = ${checkAxes}
|
101 | expect(axes).toHaveLength(expected)
|
102 | })`
|
103 | }
|
104 |
|
105 | const testCheckBars = (fixture) => {
|
106 | const { checkBars } = R.propOr({}, 'spdt', fixture)
|
107 | if (checkBars !== true) {
|
108 | return null
|
109 | }
|
110 | const checkBarsValue = R.pathOr(0, ['props', 'data', 'length'], fixture)
|
111 | return `
|
112 | it('should have ${checkBarsValue} bars according to fixture data', async () => {
|
113 | const bars = await iFrame.$$('rect.bar')
|
114 | const expected = ${checkBarsValue} // fixture.props.data.length
|
115 | expect(bars).toHaveLength(expected)
|
116 | })`
|
117 | }
|
118 |
|
119 | const testCheckArcs = (fixture) => {
|
120 | const { checkArcs } = R.propOr({}, 'spdt', fixture)
|
121 | if (checkArcs !== true) {
|
122 | return null
|
123 | }
|
124 | const checkArcsValue = R.pathOr(0, ['props', 'data', 'length'], fixture)
|
125 | return `
|
126 | it('should have ${checkArcsValue} arcs according to fixture data', async () => {
|
127 | const arcs = await iFrame.$$('path.arc')
|
128 | const expected = ${checkArcsValue} // fixture.props.data.length
|
129 | expect(arcs).toHaveLength(expected)
|
130 | })`
|
131 | }
|
132 |
|
133 |
|
134 | const testMapper = {
|
135 | checkSelector: testCheckSelector,
|
136 | checkSvg: testCheckSvg,
|
137 | checkAxes: testCheckAxes,
|
138 | checkBars: testCheckBars,
|
139 | checkArcs: testCheckArcs,
|
140 | }
|
141 |
|
142 | const generateItTests = (fixture) => {
|
143 | const result = []
|
144 | const spdtKeys = Object.keys(R.propOr({}, 'spdt', fixture))
|
145 | spdtKeys.forEach((checkItem) => testMapper[checkItem] && result.push(testMapper[checkItem](fixture)))
|
146 | return result.join('')
|
147 | }
|
148 |
|
149 | function getFileName({ file, pathToTestIndex }) {
|
150 | const config = getConfig()
|
151 | const pathTo = pathToTestIndex || config.pathToTestIndex
|
152 |
|
153 | const fileName = `${pathTo}/${file.replace('.story.', '.generated.spdt.')}`
|
154 | return fileName
|
155 | }
|
156 |
|
157 | function generateContent({ fixtures, componentName }) {
|
158 | const content = []
|
159 | content.push(generateImports(componentName))
|
160 |
|
161 | Object.keys(fixtures).forEach((fixtureName) => {
|
162 | const itTests = generateItTests(fixtures[fixtureName])
|
163 | content.push(generateDescribe(componentName, fixtureName, itTests))
|
164 | })
|
165 | return content
|
166 | }
|
167 |
|
168 | function testGenerator({ fixtures, file, componentName, pathToTestIndex }) {
|
169 | const content = generateContent({ fixtures, componentName })
|
170 | const fileName = getFileName({ file, pathToTestIndex })
|
171 | saveFile(fileName, content.join(''))
|
172 | }
|
173 |
|
174 | module.exports = {
|
175 | testGenerator,
|
176 | getFileName,
|
177 | generateContent,
|
178 | testCheckArcs,
|
179 | testCheckBars,
|
180 | testCheckSvg,
|
181 | testCheckSelector,
|
182 | testCheckAxes,
|
183 | saveFile,
|
184 | }
|