Skip to content
This repository was archived by the owner on Jun 15, 2019. It is now read-only.

Commit 7ea3fca

Browse files
committed
test(unit): Multiple outputs + check indents + check 'illegal long lines' also against vLen + etc
1. Multiple results (outputs) with different configurations will be tested. 2. indents will be tested. 3. A test was added for 'illegal long lines' that supports visual-length (not just regular length) 4. A lot of other changes (optimizations, better organizations, ...) 5. `getVisualLength` test temporarily was disabled (until a change in its source) 6. Will export results (outputs) to the ignored (.gitignore) files: "./stub/output (#)" after all tests (`afterAll()`)
1 parent 092bf3f commit 7ea3fca

File tree

8 files changed

+251
-123
lines changed

8 files changed

+251
-123
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
node_modules/
22
dist/
33
tsc-out/
4-
tempst*
4+
temp*
55
/coverage/
66
*.iml
7+
/jest/unit/stub/output (*).txt

jest/e2e/config/main.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const parentPath = path.resolve(__dirname, '..').replace(/\\/g, '/')
1010
module.exports = {
1111
rootDir: path.relative(__dirname, process.cwd()),
1212
moduleFileExtensions: ['js', 'flow'],
13-
testRegex: `${parentPath}/(?!config|mock).+?\\.js(\\.flow)?$`,
13+
testRegex: `${parentPath}/(?!config/|mock/).+?\\.js(\\.flow)?$`,
1414
globalSetup: `${__dirname}/setup.js`,
1515
globalTeardown: `${__dirname}/teardown.js`,
1616
testEnvironment: `${__dirname}/puppeteer_environment.js`,

jest/unit/config.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ module.exports = {
1313
transform: {
1414
'.(ts|tsx)': 'ts-jest'
1515
},
16-
testRegex: `${__dirname}/(?!config).+?\\.[jt]s$`,
16+
testRegex: `${__dirname}/(?!config|disabled/|temp/).+?\\.[jt]s$`,
1717
coveragePathIgnorePatterns: [
1818
'/node_modules/',
1919
'/jest/',
@@ -31,5 +31,6 @@ module.exports = {
3131
collectCoverageFrom: [
3232
'src/*.{js,ts}'
3333
],
34-
coverageDirectory: 'coverage/unit'
34+
coverageDirectory: 'coverage/unit',
35+
setupFilesAfterEnv: ['jest-expect-message'],
3536
}
File renamed without changes.

jest/unit/stub.txt renamed to jest/unit/stub/input.txt

Lines changed: 67 additions & 67 deletions
Large diffs are not rendered by default.

jest/unit/text-wrap.ts

Lines changed: 161 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as fs from 'fs'
2-
import * as path from 'path'
2+
import * as path from "path"
33
import {sha256} from 'js-sha256'
4-
import TextWrap from '../../src/TextWrap'
4+
import TextWrap, {WrapStyle} from '../../src/TextWrap'
55

66
/**
77
* @author [S. Mahdi Mir-Ismaili](https://mirismaili.github.io)
@@ -11,66 +11,175 @@ import TextWrap from '../../src/TextWrap'
1111
const inputExpectedHash = '117677f3e12ded864c527d4f03583d4dd0be3cc0542c3cbbdbb01574dcf280c8'
1212
const outputExpectedHash = '2e1bd0f9ae5b0ee9406908f58bd5b4030bbcdf464e5462e0fd1b142d49dbac2d'
1313

14-
const input = fs.readFileSync(path.resolve(__dirname, 'stub.txt'), 'utf8')
14+
const input = fs.readFileSync(path.resolve(__dirname, 'stub', 'input.txt'), 'utf8')
1515
.replace(/\r\n|\r/g, '\n')
1616

17-
const indents = ''
18-
const maxLineLength = 50
19-
const textWrapper = new TextWrap({wrapOn: maxLineLength})
20-
const wrapResult = textWrapper.wrap(input, indents)
21-
const output = wrapResult.wrappedText
17+
const allOptions: (Options | undefined)[] = [
18+
undefined,
19+
{
20+
indents: '\t',
21+
wrapOn: 50,
22+
continuationIndent: '',
23+
},
24+
{
25+
indents: '',
26+
wrapOn: 51,
27+
continuationIndent: '\t',
28+
},
29+
{
30+
indents: '',
31+
wrapOn: 52,
32+
continuationIndent: ' ',
33+
},
34+
]
2235

23-
const joinedInput = input.replace(/\n/g, '')
24-
const joinedOutput = output.replace(/\n/g, '')
36+
const outputs: string[] = []
2537

26-
describe('General tests:', () => {
27-
it('Check output itself', () => expect(joinedOutput).toBe(joinedInput))
38+
describe('Case-specific tests:', () => {
39+
it("Check input's hash", () => expect(sha256(input)).toBe(inputExpectedHash))
40+
})
41+
42+
afterAll(() => {
43+
for (let i = 0; i < allOptions.length; ++i)
44+
fs.writeFile(outputPath(i), outputs[i], err => {if (err) console.error(err)})
45+
})
46+
47+
for (let testNum = 0; testNum < allOptions.length; ++testNum) {
48+
const options = allOptions[testNum]
49+
const textWrap = new TextWrap(options)
2850

29-
it('Check num of markers', () => expect(wrapResult.markers.length).toBe(output.length - input.length))
51+
const maxLineLength = textWrap.wrapOn
52+
const continuationIndent = textWrap.continuationIndent
53+
const bc = textWrap.breakableCharactersClass
54+
const ec = textWrap.allowedExceedingCharactersClass
3055

31-
it('Try to find an illegal short line',
32-
// Two markers which [the distance between the first marker and the first breakable character after the second
33-
// marker] is less than or equal with [maxLineLength]
34-
() => {
35-
let a = 0
36-
for (let b of wrapResult.markers) {
37-
const regexp = /[^\S\xA0]/g
38-
regexp.lastIndex = b
39-
40-
const upBound = regexp.test(input) ? regexp.lastIndex - 1 : input.length
41-
const distance = textWrapper.getVisualLength(input.slice(a, upBound), 0)
42-
43-
expect(distance).toBeGreaterThan(maxLineLength)
44-
45-
a = b
46-
}
47-
}
48-
)
56+
const indents = options === undefined ? '' : options.indents
57+
const indentsN = indents + continuationIndent
4958

50-
it("Try to find an illegal long line in output", // A line that should to be wrapped, but didn't
51-
() => {
52-
const regExp = new RegExp(`^(?=.*[^\\S\\xA0\\n](?![^\\S\\xA0\\n]|$)).{${maxLineLength},}[\\S\\xA0]`,
53-
'gm') // https://regex101.com/r/bdWnCx/2/
54-
55-
expect(regExp.test(output)).toBeFalsy()
56-
})
59+
const wrapResult = textWrap.wrap(input, indents)
60+
const output = outputs[testNum] = wrapResult.wrappedText
61+
const markers = wrapResult.markers
5762

58-
it('Check markers against output', () => {
59-
let anotherOutput = ''
60-
let a = 0
61-
for (const b of wrapResult.markers) {
62-
anotherOutput += input.slice(a, b) + '\n' + indents
63-
a = b
64-
}
63+
describe(`General tests [${testNum}]:`, () => {
64+
it('Check num of markers', () => expect(output.length).toBe(input.length + markers.length * ('\n' + indentsN).length))
65+
66+
it('Reproduce output using markers', () => {
67+
let anotherOutput = ''
68+
let a = 0
69+
for (const b of markers) {
70+
anotherOutput += input.slice(a, b) + '\n' + indentsN
71+
a = b
72+
}
73+
74+
if (a > 0) anotherOutput += input.slice(a)
75+
else anotherOutput = input
76+
77+
expect(anotherOutput).toBe(output)
78+
})
79+
80+
it('Try to find an illegal short line',
81+
// Two markers which [the distance between the first marker and the first breakable character after the second
82+
// marker] is less than or equal with [maxLineLength]
83+
() => {
84+
let a = 0
85+
const regExp = new RegExp(bc.source,
86+
bc.flags.appendIfNot('g'))
87+
88+
for (let b of markers) {
89+
regExp.lastIndex = b
90+
91+
const upBound = regExp.test(input) ? regExp.lastIndex : input.length
92+
const slice = indentsN + input.slice(a, upBound) // is not strict in first cycle of the loop that hasn't indentsN
93+
const distance = textWrap.getVisualLength(slice)
94+
95+
expect(distance, `[${slice}]\n${regExp}`).toBeGreaterThan(maxLineLength)
96+
97+
a = b
98+
}
99+
})
65100

66-
if (a > 0) anotherOutput += input.slice(a)
67-
else anotherOutput = input
101+
it("Try to find an illegal long line", // A line that should to be wrapped, but hasn't
102+
() => {
103+
// https://regex101.com/r/4DaiXE/1/
104+
const regexp = new RegExp(`^([^\\n]*)(?:(?!\\n)${bc.source})[^\\n]*(?!${ec.source}).`,
105+
bc.flags.appendIfNot('g').appendIfNot('m'))
106+
107+
while (true) {
108+
const match = regexp.exec(output)
109+
if (match === null) break
110+
111+
const vLen = textWrap.getVisualLength(match[0])
112+
const wrongCondition = vLen > maxLineLength
113+
114+
if (wrongCondition &&
115+
// Check to sure the line is breakable:
116+
textWrap.getVisualLength(match[1]) > textWrap.getVisualLength(indentsN)
117+
)
118+
expect(vLen, `[${match[0]}]\n${regexp}`).toBeLessThanOrEqual(maxLineLength)
119+
}
120+
})
68121

69-
expect(anotherOutput).toBe(output)
122+
it("Try to find an illegal long line using RegExp", // Same as above but using RegExp. This one is not strict because can only calculate length, but not vLen (visual-length)
123+
() => {
124+
expect(output).not.toMatch1(
125+
// https://regex101.com/r/OfQoDb/1
126+
new RegExp(
127+
`^(?=.{${indentsN.length},}[^\\w\\xA0\\n](?![^\\S\\n]|$)).{${maxLineLength},}\\S`,
128+
'm'),
129+
`The text will be in ${outputPath(testNum)}`)
130+
})
70131
})
71-
})
132+
133+
describe(`Case-specific tests [${testNum}]:`, () => {
134+
it("Check output's hash", () => expect(sha256(output)).toBe(outputExpectedHash))
135+
})
136+
}
137+
//*************************************************************************************/
72138

73-
describe('Case-specific tests:', () => {
74-
it("Check input's hash", () => expect(sha256(input)).toBe(inputExpectedHash))
75-
it("Check output's hash", () => expect(sha256(output)).toBe(outputExpectedHash))
139+
expect.extend({
140+
toMatch1(text: string, regExp: RegExp, msg: string | (() => string)) {
141+
const match1 = regExp.exec(text)
142+
const passed = match1 !== null
143+
144+
const message = (toClause: () => string) =>
145+
() => `Expected the text to ${toClause()}\n${typeof msg === 'string' ? msg : msg()}`
146+
147+
return passed ?
148+
{
149+
message: message(() => `doesn't match ${regExp}\nFirst match:\n[${match1}]`),
150+
pass: true,
151+
} :
152+
{
153+
message: message(() => `match ${regExp}`),
154+
pass: false,
155+
}
156+
},
76157
})
158+
159+
String.prototype.appendIfNot = function (part: string): string {
160+
return this.indexOf(part) === -1 ? this as string + part : this as string
161+
}
162+
163+
declare global {
164+
namespace jest {
165+
// noinspection JSUnusedGlobalSymbols
166+
interface Matchers<R> {
167+
toMatch1(regExp: RegExp, msg: string): R
168+
}
169+
}
170+
171+
// noinspection JSUnusedGlobalSymbols
172+
interface String {
173+
appendIfNot(part: string): string;
174+
}
175+
}
176+
177+
//*************************************************************************************/
178+
179+
interface Options extends WrapStyle {
180+
indents: string
181+
}
182+
183+
function outputPath(i: number) {
184+
return path.resolve(__dirname, 'stub', `output (${i}).txt`)
185+
}

package-lock.json

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,15 @@
5050
"@babel/preset-flow": "^7.0.0",
5151
"@types/debug": "^4.1.4",
5252
"@types/jest": "^24.0.12",
53+
"@types/jest-expect-message": "^1.0.0",
5354
"@types/puppeteer": "^1.12.4",
5455
"babel-jest": "^24.8.0",
5556
"cz-conventional-changelog": "^2.1.0",
5657
"fs-extra": "^8.0.1",
5758
"jest": "^24.7.1",
5859
"jest-config": "^24.7.1",
5960
"jest-environment-node": "^24.8.0",
61+
"jest-expect-message": "^1.0.2",
6062
"js-sha256": "^0.9.0",
6163
"lodash.camelcase": "^4.3.0",
6264
"mkdirp": "^0.5.1",

0 commit comments

Comments
 (0)