Skip to content

Commit

Permalink
Move from Jest to uvu
Browse files Browse the repository at this point in the history
  • Loading branch information
ai committed Dec 30, 2021
1 parent 28289de commit e61cdb9
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 2,134 deletions.
114 changes: 59 additions & 55 deletions index.test.js
Original file line number Diff line number Diff line change
@@ -1,224 +1,226 @@
let { equal, throws } = require('uvu/assert')
let { test } = require('uvu')
let postcss = require('postcss')

let plugin = require('./')

function run (input, output, opts) {
function run(input, output, opts) {
let result = postcss([plugin(opts)]).process(input, { from: '/test.css' })
expect(result.css).toEqual(output)
expect(result.warnings()).toHaveLength(0)
equal(result.css, output)
equal(result.warnings().length, 0)
}

it('unwraps rule inside rule', () => {
test('unwraps rule inside rule', () => {
run(
'a { a: 1 } a { a: 1; b { b: 2; c { c: 3 } } }',
'a { a: 1 } a { a: 1; } a b { b: 2; } a b c { c: 3 }'
)
})

it('cleans rules after unwrap', () => {
test('cleans rules after unwrap', () => {
run('a { b .one {} b .two {} }', 'a b .one {} a b .two {}')
})

it('preserve empty rules if preserveEmpty is set to true', () => {
test('preserve empty rules if preserveEmpty is set to true', () => {
run('a { b .one {} b .two {} }', 'a { } a b .one {} a b .two {}', {
preserveEmpty: true
})
})

it('hoists at-root', () => {
test('hoists at-root', () => {
run('a { & {} @at-root { b {} } }', 'a {} b {}')
})

it('at-root short hand', () => {
test('at-root short hand', () => {
run('a { & {} @at-root b { } }', 'a {} b {}')
})

it('replaces ampersand', () => {
test('replaces ampersand', () => {
run('a { body &:hover b {} }', 'body a:hover b {}')
})

it('replaces ampersands', () => {
test('replaces ampersands', () => {
run('a { &:hover, &:active {} }', 'a:hover, a:active {}')
})

it('replaces ampersand in string', () => {
test('replaces ampersand in string', () => {
run('.block { &_elem {} }', '.block_elem {}')
})

it('unwrap rules inside at-rules', () => {
test('unwrap rules inside at-rules', () => {
run(
'@media (max-width: 500px) { a { b {} } }',
'@media (max-width: 500px) { a b {} }'
)
})

it('unwraps at-rule', () => {
test('unwraps at-rule', () => {
run(
'a { b { @media screen { width: auto } } }',
'@media screen {a b { width: auto } }'
)
})

it('unwraps at-rule with rules', () => {
test('unwraps at-rule with rules', () => {
run(
'a { @media screen { b { color: black } } }',
'@media screen { a b { color: black } }'
)
})

it('unwraps font-face to top level css', () => {
test('unwraps font-face to top level css', () => {
run(
'.a { @font-face { font-family:font; src:url() format("woff"); } }',
'@font-face { font-family:font; src:url() format("woff"); }'
)
})

it('unwraps multiple fonts to top level css', () => {
test('unwraps multiple fonts to top level css', () => {
run(
'.a { @font-face { font-family:f1; } @font-face { font-family:f2; }}',
'@font-face { font-family:f1; } @font-face { font-family:f2; }'
)
})

it('unwraps at-rules', () => {
test('unwraps at-rules', () => {
run(
'a { a: 1 } a { @media screen { @supports (a: 1) { a: 1 } } }',
'a { a: 1 } @media screen { @supports (a: 1) { a { a: 1 } } }'
)
})

it('unwraps at-rules with interleaved properties', () => {
test('unwraps at-rules with interleaved properties', () => {
run(
'a { a: 1 } a { color: red; @media screen { @supports (a: 1) { a: 1 } } background: green }',
'a { a: 1 } a { color: red; } @media screen { @supports (a: 1) { a { a: 1 } } } a { background: green }'
)
})

it('does not move custom at-rules', () => {
test('does not move custom at-rules', () => {
run(
'.one { @mixin test; } .two { @media screen { @mixin test; } } .three { @media screen { @mixin test { color: black } } } .four { @phone { color: black } }',
'.one { @mixin test; } @media screen { .two { @mixin test } } @media screen { .three { @mixin test { color: black } } } @phone { .four { color: black } }',
{ bubble: ['phone'] }
)
})

it('does not move custom at-rules placed under nested bubbling ones', () => {
test('does not move custom at-rules placed under nested bubbling ones', () => {
run(
'.one { @supports (color: black) { @media screen { @mixin test; } } } .two { @supports (color: black) { @media screen { @mixin test { color: black } } } }',
'@supports (color: black) { @media screen {.one { @mixin test } } } @supports (color: black) { @media screen { .two { @mixin test { color: black } } } }'
)
})

it('supports bubble option with at-name', () => {
test('supports bubble option with at-name', () => {
run('a { @phone { color: black } }', '@phone {a { color: black } }', {
bubble: ['@phone']
})
})

it('unwraps keyframes', () => {
test('unwraps keyframes', () => {
run(
'a { color: white; @keyframes name { to { color: black } } }',
'a { color: white; } @keyframes name { to { color: black } }'
)
})

it('supports unwrap option with at-name', () => {
test('supports unwrap option with at-name', () => {
run('a { @phone { color: black } }', '@phone { color: black }', {
unwrap: ['@phone']
})
})

it('processes comma', () => {
test('processes comma', () => {
run('.one, .two { a {} }', '.one a, .two a {}')
})

it('processes comma with ampersand', () => {
test('processes comma with ampersand', () => {
run('.one, .two { &:hover {} }', '.one:hover, .two:hover {}')
})

it('processes comma inside', () => {
test('processes comma inside', () => {
run('a, b { .one, .two {} }', 'a .one, a .two, b .one, b .two {}')
})

it('clears empty selector after comma', () => {
test('clears empty selector after comma', () => {
run('a, b { .one, .two, {} }', 'a .one, a .two, b .one, b .two {}')
})

it('moves comment with rule', () => {
test('moves comment with rule', () => {
run('a { /*B*/ b {} }', '/*B*/ a b {}')
})

it('moves comment with at-rule', () => {
test('moves comment with at-rule', () => {
run('a { /*B*/ @media { one: 1 } }', '/*B*/ @media {a { one: 1 } }')
})

it('moves comment with declaration', () => {
test('moves comment with declaration', () => {
run('a { @media { /*B*/ one: 1 } }', '@media {a { /*B*/ one: 1 } }')
})

it('saves order of rules', () => {
test('saves order of rules', () => {
run('.one { & .two {} & .tree {} }', '.one .two {} .one .tree {}')
})

it('copies rule for declarations after nested rule', () => {
test('copies rule for declarations after nested rule', () => {
run(
'a { a: 1; &b { b: 2 } c: 1; &c { d: 5 } e: 6 } c { f: 1 }',
'a { a: 1; } ab { b: 2 } a { c: 1; } ac { d: 5 } a { e: 6; } c { f: 1 }'
)
})

it('copies rule for declarations after nested rule and before at-rule', () => {
test('copies rule for declarations after nested rule and before at-rule', () => {
run(
'a { &b { a: 1 } b: 2; @media { c: 3 } }',
'ab { a: 1 } a { b: 2 } @media {a { c: 3 } }'
)
})

it('does not replace ampersand inside string', () => {
test('does not replace ampersand inside string', () => {
run(
'div { &[data-category="sound & vision"] {} }',
'div[data-category="sound & vision"] {}'
)
})

it('replaces ampersand in adjacent sibling selector', () => {
test('replaces ampersand in adjacent sibling selector', () => {
run('div { & + & {} }', 'div + div {}')
})

it('replaces ampersands in not selector', () => {
test('replaces ampersands in not selector', () => {
run('.a { &:not(&.no) {} }', '.a:not(.a.no) {}')
})

it('correctly replaces tail ampersands', () => {
test('correctly replaces tail ampersands', () => {
run('.a { .b & {} }', '.b .a {}')
})

it('correctly replaces tail ampersands that are nested further down', () => {
test('correctly replaces tail ampersands that are nested further down', () => {
run('.a { .b { .c & {} } }', '.c .a .b {}')
})

it('correctly replaces tail ampersands that are nested inside ampersand rules', () => {
test('correctly replaces tail ampersands that are nested inside ampersand rules', () => {
run('.a { &:hover { .b { .c & {} } } }', '.c .a:hover .b {}')
})

it('preserves child order when replacing tail ampersands', () => {
test('preserves child order when replacing tail ampersands', () => {
run(
'.a { color: red; .first {} @mixinFirst; .b & {} @mixinLast; .last {} }',
'.a { color: red; } .a .first {} .a { @mixinFirst; } .b .a {} .a { @mixinLast; } .a .last {}'
)
})

it('handles :host selector case', () => {
test('handles :host selector case', () => {
run(':host { &(:focus) {} }', ':host(:focus) {}')
})

it('works with other visitors', () => {
test('works with other visitors', () => {
let css = 'a{b{color:red}@mixin;}'
let mixinPlugin = () => {
return {
postcssPlugin: 'mixin',
AtRule: {
mixin (node) {
mixin(node) {
node.replaceWith('.in{.deep{color:blue}}')
}
}
Expand All @@ -228,16 +230,16 @@ it('works with other visitors', () => {
let out = postcss([plugin, mixinPlugin]).process(css, {
from: undefined
}).css
expect(out).toEqual('a b{color:red}a .in .deep{color:blue}')
equal(out, 'a b{color:red}a .in .deep{color:blue}')
})

it('works with other visitors #2', () => {
test('works with other visitors #2', () => {
let css = 'a { @mixin; b {color:red} }'
let mixinPlugin = () => {
return {
postcssPlugin: 'mixin',
AtRule: {
mixin (node) {
mixin(node) {
node.replaceWith('.in { .deep {color:blue} }')
}
}
Expand All @@ -247,30 +249,32 @@ it('works with other visitors #2', () => {
let out = postcss([plugin, mixinPlugin]).process(css, {
from: undefined
}).css
expect(out).toEqual('a .in .deep {color:blue} a b {color:red}')
equal(out, 'a .in .deep {color:blue} a b {color:red}')
})

it('shows clear errors on missed semicolon', () => {
test('shows clear errors on missed semicolon', () => {
let css = 'a{\n color: black\n @mixin b { }\n}\n'
expect(() => {
throws(() => {
css = postcss([plugin]).process(css, { from: undefined }).css
}).toThrow('2:3: Missed semicolon')
}, '2:3: Missed semicolon')
})

it('shows clear errors on other errors', () => {
test('shows clear errors on other errors', () => {
let css = 'a{\n -Option/root { }\n}\n'
expect(() => {
throws(() => {
css = postcss([plugin]).process(css, { from: undefined }).css
}).toThrow(':2:3: Unexpected')
}, ':2:3: Unexpected')
})

it('third level dependencies', () => {
test('third level dependencies', () => {
run(
'.text {&:hover{border-color: red;&:before{color: red;}}}',
'.text:hover{border-color: red;}.text:hover:before{color: red;}'
)
})

it('third level dependencies #2', () => {
test('third level dependencies #2', () => {
run('.selector{:global{h2{color:pink}}}', '.selector :global h2{color:pink}')
})

test.run()
34 changes: 15 additions & 19 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
"nested"
],
"scripts": {
"unit": "jest",
"test": "jest --coverage && eslint ."
"unit": "uvu . '\\.test\\.js$'",
"test": "c8 pnpm unit && eslint ."
},
"author": "Andrey Sitnik <andrey@sitnik.ru>",
"license": "MIT",
Expand All @@ -31,15 +31,16 @@
},
"devDependencies": {
"@logux/eslint-config": "^46.1.1",
"c8": "^7.11.0",
"clean-publish": "^3.4.4",
"eslint": "^8.5.0",
"eslint-config-standard": "^16.0.3",
"eslint-plugin-import": "^2.25.3",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prefer-let": "^3.0.1",
"eslint-plugin-promise": "^6.0.0",
"jest": "^27.4.5",
"postcss": "^8.4.5"
"postcss": "^8.4.5",
"uvu": "^0.5.2"
},
"prettier": {
"arrowParens": "avoid",
Expand All @@ -50,21 +51,16 @@
"trailingComma": "none"
},
"eslintConfig": {
"extends": "@logux/eslint-config",
"rules": {
"unicorn/better-regex": "off"
},
"globals": {
"it": "readonly",
"expect": "readonly"
}
"extends": "@logux/eslint-config"
},
"jest": {
"testEnvironment": "node",
"coverageThreshold": {
"global": {
"statements": 100
}
}
"c8": {
"exclude": [
"**/*.test.*"
],
"lines": 100,
"check-coverage": true
},
"clean-publish": {
"cleanDocs": true
}
}
Loading

0 comments on commit e61cdb9

Please sign in to comment.