added backup and email client
This commit is contained in:
+114
@@ -0,0 +1,114 @@
|
||||
import { css, glob, keyframes } from '../css';
|
||||
import { hash } from '../core/hash';
|
||||
import { compile } from '../core/compile';
|
||||
import { getSheet } from '../core/get-sheet';
|
||||
|
||||
jest.mock('../core/hash', () => ({
|
||||
hash: jest.fn().mockReturnValue('hash()')
|
||||
}));
|
||||
|
||||
jest.mock('../core/compile', () => ({
|
||||
compile: jest.fn().mockReturnValue('compile()')
|
||||
}));
|
||||
|
||||
jest.mock('../core/get-sheet', () => ({
|
||||
getSheet: jest.fn().mockReturnValue('getSheet()')
|
||||
}));
|
||||
|
||||
describe('css', () => {
|
||||
beforeEach(() => {
|
||||
hash.mockClear();
|
||||
compile.mockClear();
|
||||
getSheet.mockClear();
|
||||
});
|
||||
|
||||
it('type', () => {
|
||||
expect(typeof css).toEqual('function');
|
||||
});
|
||||
|
||||
it('args: tagged', () => {
|
||||
const out = css`base${1}`;
|
||||
|
||||
expect(compile).toBeCalledWith(['base', ''], [1], undefined);
|
||||
expect(getSheet).toBeCalled();
|
||||
expect(hash).toBeCalledWith('compile()', 'getSheet()', undefined, undefined, undefined);
|
||||
expect(out).toEqual('hash()');
|
||||
});
|
||||
|
||||
it('args: object', () => {
|
||||
const out = css({ foo: 1 });
|
||||
|
||||
expect(hash).toBeCalledWith({ foo: 1 }, 'getSheet()', undefined, undefined, undefined);
|
||||
expect(compile).not.toBeCalled();
|
||||
expect(getSheet).toBeCalled();
|
||||
expect(out).toEqual('hash()');
|
||||
});
|
||||
|
||||
it('args: array', () => {
|
||||
const propsBased = jest.fn().mockReturnValue({
|
||||
backgroundColor: 'gold'
|
||||
});
|
||||
const payload = [{ foo: 1 }, { baz: 2 }, { opacity: 0, color: 'red' }, propsBased];
|
||||
const out = css(payload);
|
||||
|
||||
expect(propsBased).toHaveBeenCalled();
|
||||
expect(hash).toBeCalledWith(
|
||||
{ foo: 1, baz: 2, opacity: 0, color: 'red', backgroundColor: 'gold' },
|
||||
'getSheet()',
|
||||
undefined,
|
||||
undefined,
|
||||
undefined
|
||||
);
|
||||
expect(compile).not.toBeCalled();
|
||||
expect(getSheet).toBeCalled();
|
||||
expect(out).toEqual('hash()');
|
||||
});
|
||||
|
||||
it('args: function', () => {
|
||||
const incoming = { foo: 'foo' };
|
||||
const out = css.call({ p: incoming }, (props) => ({ foo: props.foo }));
|
||||
|
||||
expect(hash).toBeCalledWith(incoming, 'getSheet()', undefined, undefined, undefined);
|
||||
expect(compile).not.toBeCalled();
|
||||
expect(getSheet).toBeCalled();
|
||||
expect(out).toEqual('hash()');
|
||||
});
|
||||
|
||||
it('bind', () => {
|
||||
const target = '';
|
||||
const p = {};
|
||||
const g = true;
|
||||
const out = css.bind({
|
||||
target,
|
||||
p,
|
||||
g
|
||||
})`foo: 1`;
|
||||
|
||||
expect(hash).toBeCalledWith('compile()', 'getSheet()', true, undefined, undefined);
|
||||
expect(compile).toBeCalledWith(['foo: 1'], [], p);
|
||||
expect(getSheet).toBeCalledWith(target);
|
||||
expect(out).toEqual('hash()');
|
||||
});
|
||||
});
|
||||
|
||||
describe('glob', () => {
|
||||
it('type', () => {
|
||||
expect(typeof glob).toEqual('function');
|
||||
});
|
||||
|
||||
it('args: g', () => {
|
||||
glob`a:b`;
|
||||
expect(hash).toBeCalledWith('compile()', 'getSheet()', 1, undefined, undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('keyframes', () => {
|
||||
it('type', () => {
|
||||
expect(typeof keyframes).toEqual('function');
|
||||
});
|
||||
|
||||
it('args: k', () => {
|
||||
keyframes`a:b`;
|
||||
expect(hash).toBeCalledWith('compile()', 'getSheet()', undefined, undefined, 1);
|
||||
});
|
||||
});
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import * as goober from '../index';
|
||||
|
||||
describe('goober', () => {
|
||||
it('exports', () => {
|
||||
expect(Object.keys(goober).sort()).toEqual([
|
||||
'css',
|
||||
'extractCss',
|
||||
'glob',
|
||||
'keyframes',
|
||||
'setup',
|
||||
'styled'
|
||||
]);
|
||||
});
|
||||
});
|
||||
+246
@@ -0,0 +1,246 @@
|
||||
import { h, createContext, render } from 'preact';
|
||||
import { useContext, forwardRef } from 'preact/compat';
|
||||
import { setup, css, styled, keyframes } from '../index';
|
||||
import { extractCss } from '../core/update';
|
||||
|
||||
describe('integrations', () => {
|
||||
it('preact', () => {
|
||||
const ThemeContext = createContext();
|
||||
const useTheme = () => useContext(ThemeContext);
|
||||
|
||||
setup(h, null, useTheme);
|
||||
|
||||
const target = document.createElement('div');
|
||||
|
||||
const Span = styled('span', forwardRef)`
|
||||
color: red;
|
||||
`;
|
||||
|
||||
const SpanWrapper = styled('div')`
|
||||
color: cyan;
|
||||
|
||||
${Span} {
|
||||
border: 1px solid red;
|
||||
}
|
||||
`;
|
||||
|
||||
const BoxWithColor = styled('div')`
|
||||
color: ${(props) => props.color};
|
||||
`;
|
||||
|
||||
const BoxWithColorFn = styled('div')(
|
||||
(props) => `
|
||||
color: ${props.color};
|
||||
`
|
||||
);
|
||||
|
||||
const BoxWithThemeColor = styled('div')`
|
||||
color: ${(props) => props.theme.color};
|
||||
`;
|
||||
|
||||
const BoxWithThemeColorFn = styled('div')(
|
||||
(props) => `
|
||||
color: ${props.theme.color};
|
||||
`
|
||||
);
|
||||
|
||||
const fadeAnimation = keyframes`
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
99% {
|
||||
opacity: 1;
|
||||
color: dodgerblue;
|
||||
}
|
||||
`;
|
||||
|
||||
const BoxWithAnimation = styled('span')`
|
||||
opacity: 0;
|
||||
animation: ${fadeAnimation} 500ms ease-in-out;
|
||||
`;
|
||||
|
||||
const BoxWithConditionals = styled('div')([
|
||||
{ foo: 1 },
|
||||
(props) => ({ color: props.isActive ? 'red' : 'tomato' }),
|
||||
null,
|
||||
{ baz: 0 },
|
||||
false,
|
||||
{ baz: 0 }
|
||||
]);
|
||||
|
||||
const shared = { opacity: 0 };
|
||||
const BoxWithShared = styled('div')(shared);
|
||||
const BoxWithSharedAndConditional = styled('div')([shared, { baz: 0 }]);
|
||||
|
||||
const BoxWithHas = styled('div')`
|
||||
label:has(input, select),
|
||||
:has(foo, boo) {
|
||||
color: red;
|
||||
}
|
||||
`;
|
||||
|
||||
const refSpy = jest.fn();
|
||||
|
||||
render(
|
||||
<ThemeContext.Provider value={{ color: 'blue' }}>
|
||||
<div>
|
||||
<Span ref={refSpy} />
|
||||
<Span as={'div'} />
|
||||
<SpanWrapper>
|
||||
<Span />
|
||||
</SpanWrapper>
|
||||
<BoxWithColor color={'red'} />
|
||||
<BoxWithColorFn color={'red'} />
|
||||
<BoxWithThemeColor />
|
||||
<BoxWithThemeColorFn />
|
||||
<BoxWithThemeColor theme={{ color: 'green' }} />
|
||||
<BoxWithThemeColorFn theme={{ color: 'orange' }} />
|
||||
<BoxWithAnimation />
|
||||
<BoxWithConditionals isActive />
|
||||
<BoxWithShared />
|
||||
<BoxWithSharedAndConditional />
|
||||
<div className={css([shared, { background: 'cyan' }])} />
|
||||
<BoxWithHas />
|
||||
</div>
|
||||
</ThemeContext.Provider>,
|
||||
target
|
||||
);
|
||||
|
||||
expect(extractCss()).toMatchInlineSnapshot(
|
||||
[
|
||||
'"',
|
||||
' ', // Empty white space that holds the textNode that the styles are appended
|
||||
'@keyframes go384228713{0%{opacity:0;}99%{opacity:1;color:dodgerblue;}}',
|
||||
'.go1127809067{opacity:0;background:cyan;}',
|
||||
'.go3865451590{color:red;}',
|
||||
'.go3991234422{color:cyan;}',
|
||||
'.go3991234422 .go3865451590{border:1px solid red;}',
|
||||
'.go1925576363{color:blue;}',
|
||||
'.go3206651468{color:green;}',
|
||||
'.go4276997079{color:orange;}',
|
||||
'.go2069586824{opacity:0;animation:go384228713 500ms ease-in-out;}',
|
||||
'.go631307347{foo:1;color:red;baz:0;}',
|
||||
'.go3865943372{opacity:0;}',
|
||||
'.go1162430001{opacity:0;baz:0;}',
|
||||
'.go2602823658 label:has(input, select),.go2602823658 :has(foo, boo){color:red;}',
|
||||
'"'
|
||||
].join('')
|
||||
);
|
||||
|
||||
expect(refSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
tagName: 'SPAN'
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('support extending with as', () => {
|
||||
const list = ['p', 'm', 'as', 'bg'];
|
||||
setup(h, undefined, undefined, (props) => {
|
||||
for (let prop in props) {
|
||||
if (list.indexOf(prop) !== -1) {
|
||||
delete props[prop];
|
||||
}
|
||||
}
|
||||
});
|
||||
const target = document.createElement('div');
|
||||
|
||||
const Base = styled('div')(({ p = 0, m }) => [
|
||||
{
|
||||
color: 'white',
|
||||
padding: p + 'em'
|
||||
},
|
||||
m != null && { margin: m + 'em' }
|
||||
]);
|
||||
|
||||
const Super = styled(Base)`
|
||||
background: ${(p) => p.bg || 'none'};
|
||||
`;
|
||||
|
||||
render(
|
||||
<div>
|
||||
<Base />
|
||||
<Base p={2} />
|
||||
<Base m={1} p={3} as={'span'} />
|
||||
<Super m={1} bg={'dodgerblue'} as={'button'} />
|
||||
</div>,
|
||||
target
|
||||
);
|
||||
|
||||
// Makes sure the resulting DOM does not contain any props
|
||||
expect(target.innerHTML).toEqual(
|
||||
[
|
||||
'<div>',
|
||||
'<div class="go103173764"></div>',
|
||||
'<div class="go103194166"></div>',
|
||||
'<span class="go2081835032"></span>',
|
||||
'<button class="go1969245729 go1824201605"></button>',
|
||||
'</div>'
|
||||
].join('')
|
||||
);
|
||||
|
||||
expect(extractCss()).toMatchInlineSnapshot(
|
||||
[
|
||||
'"',
|
||||
'.go1969245729{color:white;padding:0em;margin:1em;}',
|
||||
'.go103173764{color:white;padding:0em;}',
|
||||
'.go103194166{color:white;padding:2em;}',
|
||||
'.go2081835032{color:white;padding:3em;margin:1em;}',
|
||||
'.go1824201605{background:dodgerblue;}',
|
||||
'"'
|
||||
].join('')
|
||||
);
|
||||
});
|
||||
|
||||
it('shouldForwardProps', () => {
|
||||
const list = ['p', 'm', 'as'];
|
||||
setup(h, undefined, undefined, (props) => {
|
||||
for (let prop in props) {
|
||||
if (list.indexOf(prop) !== -1) {
|
||||
delete props[prop];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const target = document.createElement('div');
|
||||
|
||||
const Base = styled('div')(({ p = 0, m }) => [
|
||||
{
|
||||
color: 'white',
|
||||
padding: p + 'em'
|
||||
},
|
||||
m != null && { margin: m + 'em' }
|
||||
]);
|
||||
|
||||
render(
|
||||
<div>
|
||||
<Base />
|
||||
<Base p={2} />
|
||||
<Base m={1} p={3} as={'span'} />
|
||||
</div>,
|
||||
target
|
||||
);
|
||||
|
||||
// Makes sure the resulting DOM does not contain any props
|
||||
expect(target.innerHTML).toEqual(
|
||||
[
|
||||
'<div>',
|
||||
'<div class="go103173764"></div>',
|
||||
'<div class="go103194166"></div>',
|
||||
'<span class="go2081835032"></span>',
|
||||
'</div>'
|
||||
].join(''),
|
||||
`"<div><div class=\\"go103173764\\"></div><div class=\\"go103194166\\"></div><span class=\\"go2081835032\\"></span></div>"`
|
||||
);
|
||||
|
||||
expect(extractCss()).toMatchInlineSnapshot(
|
||||
[
|
||||
'"',
|
||||
'.go103173764{color:white;padding:0em;}',
|
||||
'.go103194166{color:white;padding:2em;}',
|
||||
'.go2081835032{color:white;padding:3em;margin:1em;}',
|
||||
'"'
|
||||
].join('')
|
||||
);
|
||||
});
|
||||
});
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
import { styled, setup } from '../styled';
|
||||
import { extractCss } from '../core/update';
|
||||
|
||||
const pragma = jest.fn((tag, props) => {
|
||||
return { tag, props: { ...props, className: props.className.replace(/go\d+/g, 'go') } };
|
||||
});
|
||||
|
||||
expect.extend({
|
||||
toMatchVNode(received, tag, props) {
|
||||
expect(received.tag).toEqual(tag);
|
||||
expect(received.props).toEqual(props);
|
||||
return {
|
||||
message: 'Expected vnode to match vnode',
|
||||
pass: true
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
describe('styled', () => {
|
||||
beforeEach(() => {
|
||||
pragma.mockClear();
|
||||
setup(pragma);
|
||||
extractCss();
|
||||
});
|
||||
|
||||
it('calls pragma', () => {
|
||||
setup(undefined);
|
||||
expect(() => styled()()()).toThrow();
|
||||
|
||||
setup(pragma);
|
||||
const vnode = styled('div')``();
|
||||
|
||||
expect(pragma).toBeCalledTimes(1);
|
||||
expect(vnode).toMatchVNode('div', {
|
||||
className: 'go'
|
||||
});
|
||||
});
|
||||
|
||||
it('extend props', () => {
|
||||
const vnode = styled('tag')`
|
||||
color: peachpuff;
|
||||
`({ bar: 1 });
|
||||
|
||||
expect(vnode).toMatchVNode('tag', {
|
||||
bar: 1,
|
||||
className: 'go'
|
||||
});
|
||||
expect(extractCss()).toEqual('.go3183460609{color:peachpuff;}');
|
||||
});
|
||||
|
||||
it('concat className if present in props', () => {
|
||||
const vnode = styled('tag')`
|
||||
color: peachpuff;
|
||||
`({ bar: 1, className: 'existing' });
|
||||
|
||||
expect(vnode).toMatchVNode('tag', {
|
||||
bar: 1,
|
||||
className: 'go existing'
|
||||
});
|
||||
});
|
||||
|
||||
it('pass template function', () => {
|
||||
const vnode = styled('tag')((props) => ({ color: props.color }))({ color: 'red' });
|
||||
|
||||
expect(vnode).toMatchVNode('tag', {
|
||||
className: 'go',
|
||||
color: 'red'
|
||||
});
|
||||
expect(extractCss()).toEqual('.go3433634237{color:red;}');
|
||||
});
|
||||
|
||||
it('change tag via "as" prop', () => {
|
||||
const Tag = styled('tag')`
|
||||
color: red;
|
||||
`;
|
||||
|
||||
// Simulate a render
|
||||
let vnode = Tag();
|
||||
expect(vnode).toMatchVNode('tag', { className: 'go' });
|
||||
|
||||
// Simulate a render with
|
||||
vnode = Tag({ as: 'foo' });
|
||||
// Expect it to be changed to foo
|
||||
expect(vnode).toMatchVNode('foo', { className: 'go' });
|
||||
|
||||
// Simulate a render
|
||||
vnode = Tag();
|
||||
expect(vnode).toMatchVNode('tag', { className: 'go' });
|
||||
});
|
||||
|
||||
it('support forwardRef', () => {
|
||||
const forwardRef = jest.fn((fn) => (props) => fn(props, 'ref'));
|
||||
const vnode = styled('tag', forwardRef)`
|
||||
color: red;
|
||||
`({ bar: 1 });
|
||||
|
||||
expect(vnode).toMatchVNode('tag', {
|
||||
bar: 1,
|
||||
className: 'go',
|
||||
ref: 'ref'
|
||||
});
|
||||
});
|
||||
|
||||
it('setup useTheme', () => {
|
||||
setup(pragma, null, () => 'theme');
|
||||
|
||||
const styleFn = jest.fn(() => ({}));
|
||||
const vnode = styled('tag')(styleFn)({ bar: 1 });
|
||||
|
||||
expect(styleFn).toBeCalledWith({ bar: 1, theme: 'theme' });
|
||||
expect(vnode).toMatchVNode('tag', {
|
||||
bar: 1,
|
||||
className: 'go'
|
||||
});
|
||||
});
|
||||
|
||||
it('setup useTheme with theme prop override', () => {
|
||||
setup(pragma, null, () => 'theme');
|
||||
|
||||
const styleFn = jest.fn(() => ({}));
|
||||
const vnode = styled('tag')(styleFn)({ theme: 'override' });
|
||||
|
||||
expect(styleFn).toBeCalledWith({ theme: 'override' });
|
||||
expect(vnode).toMatchVNode('tag', { className: 'go', theme: 'override' });
|
||||
});
|
||||
|
||||
it('uses babel compiled classNames', () => {
|
||||
const Comp = styled('tag')``;
|
||||
Comp.className = 'foobar';
|
||||
const vnode = Comp({});
|
||||
expect(vnode).toMatchVNode('tag', { className: 'go foobar' });
|
||||
});
|
||||
|
||||
it('omits css prop with falsy should forward prop function', () => {
|
||||
const shouldForwardProp = (props) => {
|
||||
for (let prop in props) {
|
||||
if (prop.includes('$')) delete props[prop];
|
||||
}
|
||||
};
|
||||
// Overwrite setup for this test
|
||||
setup(pragma, undefined, undefined, shouldForwardProp);
|
||||
|
||||
const vnode = styled('tag')`
|
||||
color: peachpuff;
|
||||
`({ bar: 1, $templateColumns: '1fr 1fr' });
|
||||
|
||||
expect(vnode).toMatchVNode('tag', { className: 'go', bar: 1 });
|
||||
});
|
||||
|
||||
it('pass truthy logical and operator', () => {
|
||||
const Tag = styled('tag')((props) => props.draw && { color: 'yellow' });
|
||||
|
||||
// Simulate a render
|
||||
let vnode = Tag({ draw: true });
|
||||
|
||||
expect(vnode).toMatchVNode('tag', { className: 'go', draw: true });
|
||||
expect(extractCss()).toEqual('.go2986228274{color:yellow;}');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user