UNPKG

6.48 kBJavaScriptView Raw
1const R = require('ramda')
2const fs = require('fs')
3const path = require('path')
4const debug = require('debug')('spdt')
5const { getConfig, STORYBOOK_PORT, SPDT_DIR } = require('./config')
6
7const saveFile = (fileName, content) => {
8 fs.writeFile(fileName, content, (err) => {
9 if (err) {
10 return debug(`error: ${err}`)
11 } else {
12 return debug(`File was saved: ${fileName}`)
13 }
14 })
15}
16
17const generateImports = () => `const TIMEOUT = 5000
18`
19
20const generateDescribe = (componentName, fixtureName, itTests) => `
21describe(
22 '${componentName} - fixture ${fixtureName}',
23 () => {
24 let page
25 beforeEach(async () => {
26 // eslint-disable-next-line no-underscore-dangle
27 page = await global.__BROWSER__.newPage()
28 // prettier-ignore
29 await page.goto(
30 'http://localhost:${STORYBOOK_PORT}/iframe.html?path=/story/${componentName.toLowerCase()}--${fixtureName.toLowerCase()}',
31 { waitUntil: 'load' }
32 )
33 }, TIMEOUT)
34
35 afterEach(async () => {
36 await page.close()
37 })
38${itTests}
39 },
40 TIMEOUT,
41)
42`
43
44const testCheckSelectorObject = (strOrObj) => {
45 let selector
46 let length
47 if (typeof strOrObj === 'string') {
48 selector = strOrObj
49 length = 1
50 } else if (typeof strOrObj === 'object' && !Array.isArray(strOrObj)) {
51 ;({ length, selector } = strOrObj)
52 }
53
54 if (!selector || !length) {
55 return null
56 }
57 return `
58 it('checkSelector: should find component matching selector [${selector}] ${length} time(s)', async () => {
59 const components = await page.$$('${selector}')
60 const expected = ${length}
61 expect(components).toHaveLength(expected)
62 })`
63}
64
65const testCheckSelector = (fixture) => {
66 const { checkSelector } = R.propOr({}, 'spdt', fixture)
67 if (!checkSelector) {
68 return null
69 }
70 if (typeof checkSelector === 'string') {
71 return testCheckSelectorObject(checkSelector)
72 }
73 if (Array.isArray(checkSelector)) {
74 return checkSelector.map(testCheckSelectorObject)
75 } else if (typeof checkSelector === 'object') {
76 return testCheckSelectorObject(checkSelector)
77 }
78 return null
79}
80
81const testCheckAttrObject = ({ selector, attribute, expected }) => {
82 if (!selector || !expected || !attribute) {
83 return null
84 }
85 return `
86 it('checkAttr: should find component matching selector [${selector}] with attribute [${attribute}]', async () => {
87 const actual = await page.$eval('${selector}', element => element.getAttribute('${attribute}'))
88 expect(actual).toEqual('${expected}')
89 })`
90}
91
92const testCheckAttr = (fixture) => {
93 const { checkAttr } = (fixture && fixture.spdt) || {}
94 if (!checkAttr) {
95 return null
96 }
97 if (Array.isArray(checkAttr)) {
98 return checkAttr.map(testCheckAttrObject)
99 } else if (typeof checkAttr === 'object') {
100 return testCheckAttrObject(checkAttr)
101 }
102 return null
103}
104
105const testCheckSvg = (fixture) => {
106 const { checkSvg } = R.propOr({}, 'spdt', fixture)
107 if (checkSvg !== true) {
108 return null
109 }
110 return `
111 it('checkSvg: should load component as <svg>', async () => {
112 const component = await page.$('svg')
113 expect(component._remoteObject.description).toMatch('svg') // eslint-disable-line no-underscore-dangle
114 })`
115}
116
117const testCheckAxes = (fixture) => {
118 const { checkAxes } = R.propOr({}, 'spdt', fixture)
119 if (!Number.isInteger(checkAxes)) {
120 return null
121 }
122 return `
123 it('checkAxes: should have ${checkAxes} axes', async () => {
124 const axes = await page.$$('g.axis')
125 const expected = ${checkAxes}
126 expect(axes).toHaveLength(expected)
127 })`
128}
129
130const testCheckBars = (fixture) => {
131 const { checkBars } = R.propOr({}, 'spdt', fixture)
132 if (checkBars !== true) {
133 return null
134 }
135 const checkBarsValue = R.pathOr(0, ['props', 'data', 'length'], fixture)
136 return `
137 it('checkBars: should have ${checkBarsValue} bars according to fixture data', async () => {
138 const bars = await page.$$('rect.bar')
139 const expected = ${checkBarsValue} // fixture.props.data.length
140 expect(bars).toHaveLength(expected)
141 })`
142}
143
144const testCheckArcs = (fixture) => {
145 const { checkArcs } = R.propOr({}, 'spdt', fixture)
146 if (checkArcs !== true) {
147 return null
148 }
149 const checkArcsValue = R.pathOr(0, ['props', 'data', 'length'], fixture)
150 return `
151 it('checkArcs: should have ${checkArcsValue} arcs according to fixture data', async () => {
152 const arcs = await page.$$('path.arc')
153 const expected = ${checkArcsValue} // fixture.props.data.length
154 expect(arcs).toHaveLength(expected)
155 })`
156}
157
158const config = getConfig()
159let customMapper
160try {
161 const mapperPath = path.resolve(
162 config.projectRoot,
163 SPDT_DIR,
164 'test-declarations.js',
165 )
166 // eslint-disable-next-line import/no-dynamic-require
167 customMapper = require(mapperPath)
168} catch (error) {
169 debug(`Caught exception: ${error.message}`)
170 throw error
171}
172
173// map a key in fixtures.[fixture name].spdt to respective function which generates an it test
174const testMapper = {
175 checkSelector: testCheckSelector,
176 checkSvg: testCheckSvg,
177 checkAxes: testCheckAxes,
178 checkBars: testCheckBars,
179 checkArcs: testCheckArcs,
180 ...customMapper,
181}
182
183const generateItTests = (fixture) => {
184 const result = []
185 const spdtKeys = Object.keys(R.propOr({}, 'spdt', fixture))
186 spdtKeys.forEach((checkItem) => {
187 if (testMapper[checkItem]) {
188 const test = testMapper[checkItem](fixture)
189 result.push(Array.isArray(test) ? test.join('') : test)
190 }
191 })
192 return result.join('')
193}
194
195function getFileName({ file, pathToTestIndex }) {
196 const pathTo = pathToTestIndex || getConfig().pathToTestIndex
197 return `${pathTo}/${file.replace('.story.', '.generated.spdt.')}`
198}
199
200function generateContent({ fixtures, componentName }) {
201 const content = []
202 content.push(generateImports(componentName))
203
204 Object.keys(fixtures).forEach((fixtureName) => {
205 const itTests = generateItTests(fixtures[fixtureName])
206 content.push(generateDescribe(componentName, fixtureName, itTests))
207 })
208 return content
209}
210
211function testGenerator({ fixtures, file, componentName, pathToTestIndex }) {
212 const content = generateContent({ fixtures, componentName })
213 const fileName = getFileName({ file, pathToTestIndex })
214 saveFile(fileName, content.join(''))
215}
216
217module.exports = {
218 testGenerator,
219 getFileName,
220 generateContent,
221 generateItTests,
222 testCheckArcs,
223 testCheckBars,
224 testCheckSvg,
225 testCheckSelector,
226 testCheckAxes,
227 saveFile,
228 testCheckAttr,
229}