From e516bf4ec700538904a27a81723fbb4c797c5593 Mon Sep 17 00:00:00 2001 From: Pedro Yamada Date: Tue, 19 Mar 2024 16:38:07 +1100 Subject: [PATCH 1/5] Write a few unit-tests for babel plugin --- .../src/__tests__/css-builder.test.ts | 119 +++++++++++++++++ .../src/utils/__tests__/css-builders.test.ts | 122 ++++++++++++++++++ .../__tests__/evaluate-expression.test.ts | 63 +++++++++ 3 files changed, 304 insertions(+) create mode 100644 packages/babel-plugin/src/utils/__tests__/css-builders.test.ts create mode 100644 packages/babel-plugin/src/utils/__tests__/evaluate-expression.test.ts diff --git a/packages/babel-plugin/src/__tests__/css-builder.test.ts b/packages/babel-plugin/src/__tests__/css-builder.test.ts index 4519ad912..55fadcfc6 100644 --- a/packages/babel-plugin/src/__tests__/css-builder.test.ts +++ b/packages/babel-plugin/src/__tests__/css-builder.test.ts @@ -49,4 +49,123 @@ describe('css builder', () => { " `); }); + + it('works in spite of a style override', () => { + const actual = transform(` + import { css } from '@compiled/react'; + + const styles = css({ color: ({ color }) => color }); + + function Component({ color }) { + return
+ } + `); + + expect(actual).toMatchInlineSnapshot(` + "import * as React from "react"; + import { ax, ix, CC, CS } from "@compiled/react/runtime"; + const _ = "._syaz1cj8{color:var(--_xexnhp)}"; + const styles = null; + function Component({ color }) { + return ( + + {[_]} + { +
__cmplp.color), + }} + /> + } + + ); + } + " + `); + }); + + it('works when there is a clear member expression', () => { + const actual = transform(` + import { css } from '@compiled/react'; + + const styles = { + test: { + red: css({ color: 'red' }), + blue: css({ color: 'blue' }), + }, + }; + + function Component({ color }) { + return
+ } + `); + + expect(actual).toMatchInlineSnapshot(` + "import * as React from "react"; + import { ax, ix, CC, CS } from "@compiled/react/runtime"; + const _ = "._syaz5scu{color:red}"; + const styles = { + test: { + red: null, + blue: null, + }, + }; + function Component({ color }) { + return ( + + {[_]} + {
} + + ); + } + " + `); + }); + + it.only('does not work when there is simple logic to get to the style', () => { + const actual = transform(` + import { css } from '@compiled/react'; + + const styles = { + test: { + red: css({ color: 'red' }), + blue: css({ color: 'blue' }), + }, + }; + + function Component({ color }) { + return
+ } + `); + + expect(actual).toMatchInlineSnapshot(` + "import * as React from "react"; + import { ax, ix, CC, CS } from "@compiled/react/runtime"; + const _ = "._syaz5scu{color:red}"; + const styles = { + test: { + red: null, + blue: null, + }, + }; + function Component({ color }) { + return ( + + {[_]} + { +
+ } + + ); + } + " + `); + }); }); diff --git a/packages/babel-plugin/src/utils/__tests__/css-builders.test.ts b/packages/babel-plugin/src/utils/__tests__/css-builders.test.ts new file mode 100644 index 000000000..d73ee7179 --- /dev/null +++ b/packages/babel-plugin/src/utils/__tests__/css-builders.test.ts @@ -0,0 +1,122 @@ +import generate from '@babel/generator'; +import { parse } from '@babel/parser'; +import type { NodePath } from '@babel/traverse'; +import traverse from '@babel/traverse'; +import type { Identifier, MemberExpression } from '@babel/types'; + +import type { Metadata } from '../../types'; +import { buildCss } from '../css-builders'; + +describe('buildCss', () => { + it('returns a css string and variables array for an identifier node', () => { + const file = parse(` + const color = { background: 'red' }; + const styles = color; + + run(styles); + `); + + let path: NodePath | null = null; + traverse(file, { + CallExpression(nodePath) { + nodePath.traverse({ + Identifier(innerPath) { + if (innerPath.node.name === 'styles') { + path = innerPath; + } + }, + }); + }, + }); + + expect(path).not.toEqual(null); + const meta: Metadata = { + parentPath: path!.parentPath, + state: { + filename: '', + }, + } as any; + + const { css, variables } = buildCss(path!.node, meta); + expect(css).toEqual([{ css: 'background: red;', type: 'unconditional' }]); + expect(variables).toEqual([]); + }); + + it('returns a css string and variables array for a member expression node', () => { + const file = parse(` + const styles = { option1: { background: 'red' } }; + + run(styles.option1); + `); + + let path: NodePath | null = null; + traverse(file, { + CallExpression(nodePath) { + nodePath.traverse({ + MemberExpression(innerPath) { + path = innerPath; + }, + }); + }, + }); + + expect(path).not.toEqual(null); + + const meta: Metadata = { + parentPath: path!.parentPath, + state: { + cssMap: {}, + filename: '', + }, + } as any; + + const { css, variables } = buildCss(path!.node, meta); + expect(css).toEqual([{ css: 'background: red;', type: 'unconditional' }]); + expect(variables).toEqual([]); + }); + + it('returns a css string for a variable member expression', () => { + const file = parse( + ` + import { css } from '@compiled/react'; + + const styles = { option1: css({ background: 'red' }) }; + + run(styles[key]); + `, + { sourceType: 'module' } + ); + + let path: NodePath | null = null; + traverse(file, { + CallExpression(nodePath) { + nodePath.traverse({ + MemberExpression(innerPath) { + path = innerPath; + }, + }); + }, + }); + + expect(path).not.toEqual(null); + + const meta: Metadata = { + parentPath: path!.parentPath, + state: { + cssMap: {}, + filename: '', + }, + } as any; + + const { css, variables } = buildCss(path!.node, meta); + // TODO: This should not happen + expect(css).toEqual([{ css: 'option1: var(--_g48cyt);', type: 'unconditional' }]); + expect(variables.length).toEqual(1); + expect(variables[0].name).toEqual('--_g48cyt'); + expect(generate(variables[0].expression).code).toMatchInlineSnapshot(` + "css({ + background: 'red' + })" + `); + }); +}); diff --git a/packages/babel-plugin/src/utils/__tests__/evaluate-expression.test.ts b/packages/babel-plugin/src/utils/__tests__/evaluate-expression.test.ts new file mode 100644 index 000000000..b22e1a3ca --- /dev/null +++ b/packages/babel-plugin/src/utils/__tests__/evaluate-expression.test.ts @@ -0,0 +1,63 @@ +import { parse } from '@babel/parser'; +import type { NodePath } from '@babel/traverse'; +import traverse from '@babel/traverse'; +import type { Identifier, MemberExpression } from '@babel/types'; +import { identifier, memberExpression, stringLiteral } from '@babel/types'; + +import type { Metadata } from '../../types'; +import { evaluateExpression } from '../evaluate-expression'; + +describe('evaluateExpression', () => { + it('should evaluate a variable reference to its value', () => { + const file = parse(` + const color = 'red'; + const styles = color; + + run(styles); + `); + + let path: NodePath | null = null; + traverse(file, { + CallExpression(nodePath) { + nodePath.traverse({ + Identifier(innerPath) { + if (innerPath.node.name === 'styles') { + path = innerPath; + } + }, + }); + }, + }); + + expect(path).not.toEqual(null); + const meta: Metadata = { + parentPath: path!.parentPath, + } as any; + + const { value } = evaluateExpression(path!.node, meta); + expect(value).toEqual(stringLiteral('red')); + }); + + it('should evaluate a member expression', () => { + const file = parse(` + const styles = foo.bar; + `); + + let path: NodePath | null = null; + traverse(file, { + MemberExpression(nodePath) { + path = nodePath; + }, + }); + + expect(path).not.toEqual(null); + const meta: Metadata = { + parentPath: path!.parentPath, + } as any; + + const { value } = evaluateExpression(path!.node, meta); + const expected = memberExpression(identifier('foo'), identifier('bar')); + delete expected.optional; + expect(value).toMatchObject(expected); + }); +}); From e5e425589c7f06c8959a1a6bcd6c40aaa291b2e9 Mon Sep 17 00:00:00 2001 From: Pedro Yamada Date: Tue, 19 Mar 2024 16:56:09 +1100 Subject: [PATCH 2/5] Fix newly added tests --- .../src/__tests__/css-builder.test.ts | 81 ++++++++++++------- 1 file changed, 54 insertions(+), 27 deletions(-) diff --git a/packages/babel-plugin/src/__tests__/css-builder.test.ts b/packages/babel-plugin/src/__tests__/css-builder.test.ts index 55fadcfc6..f348927ef 100644 --- a/packages/babel-plugin/src/__tests__/css-builder.test.ts +++ b/packages/babel-plugin/src/__tests__/css-builder.test.ts @@ -50,6 +50,38 @@ describe('css builder', () => { `); }); + it('works with css map', () => { + const actual = transform(` + import { cssMap } from '@compiled/react'; + + const styles = cssMap({ red: { color: 'red' }, blue: { color: 'blue' } }); + + function Component({ color }) { + return
+ } + `); + + expect(actual).toMatchInlineSnapshot(` + "import * as React from "react"; + import { ax, ix, CC, CS } from "@compiled/react/runtime"; + const _2 = "._syaz13q2{color:blue}"; + const _ = "._syaz5scu{color:red}"; + const styles = { + red: "_syaz5scu", + blue: "_syaz13q2", + }; + function Component({ color }) { + return ( + + {[_, _2]} + {
} + + ); + } + " + `); + }); + it('works in spite of a style override', () => { const actual = transform(` import { css } from '@compiled/react'; @@ -124,8 +156,9 @@ describe('css builder', () => { `); }); - it.only('does not work when there is simple logic to get to the style', () => { - const actual = transform(` + it('does not work when there is logic to get to the style', () => { + const actual = () => + transform(` import { css } from '@compiled/react'; const styles = { @@ -140,31 +173,25 @@ describe('css builder', () => { } `); - expect(actual).toMatchInlineSnapshot(` - "import * as React from "react"; - import { ax, ix, CC, CS } from "@compiled/react/runtime"; - const _ = "._syaz5scu{color:red}"; - const styles = { - test: { - red: null, - blue: null, - }, - }; - function Component({ color }) { - return ( - - {[_]} - { -
- } - - ); - } + expect(actual).toThrowErrorMatchingInlineSnapshot(` + "unknown file: + ██████╗ ██████╗ ███╗ ███╗██████╗ ██╗██╗ ███████╗██████╗ + ██╔════╝██╔═══██╗████╗ ████║██╔══██╗██║██║ ██╔════╝██╔══██╗ + ██║ ██║ ██║██╔████╔██║██████╔╝██║██║ █████╗ ██║ ██║ + ██║ ██║ ██║██║╚██╔╝██║██╔═══╝ ██║██║ ██╔══╝ ██║ ██║ + ╚██████╗╚██████╔╝██║ ╚═╝ ██║██║ ██║███████╗███████╗██████╔╝ + ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝╚═════╝ + + @compiled/css - Unhandled exception + + An unhandled exception was raised when parsing your CSS, this is probably a bug! + Raise an issue here: https://github.com/atlassian-labs/compiled/issues/new?assignees=&labels=&template=bug_report.md&title=CSS%20Parsing%20Exception:%20 + + Input CSS: { + red + } + + Exception: :1:1: Unknown word " `); }); From dc82e49f9a0a86468c5a85dc86dd0451df79ec90 Mon Sep 17 00:00:00 2001 From: Pedro Tacla Yamada Date: Wed, 20 Mar 2024 13:44:33 +1100 Subject: [PATCH 3/5] Remove error snapshot from tests Co-authored-by: Grant Wong <2908767+dddlr@users.noreply.github.com> --- .../src/__tests__/css-builder.test.ts | 22 +------------------ 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/packages/babel-plugin/src/__tests__/css-builder.test.ts b/packages/babel-plugin/src/__tests__/css-builder.test.ts index f348927ef..4dcbb74bf 100644 --- a/packages/babel-plugin/src/__tests__/css-builder.test.ts +++ b/packages/babel-plugin/src/__tests__/css-builder.test.ts @@ -173,26 +173,6 @@ describe('css builder', () => { } `); - expect(actual).toThrowErrorMatchingInlineSnapshot(` - "unknown file: - ██████╗ ██████╗ ███╗ ███╗██████╗ ██╗██╗ ███████╗██████╗ - ██╔════╝██╔═══██╗████╗ ████║██╔══██╗██║██║ ██╔════╝██╔══██╗ - ██║ ██║ ██║██╔████╔██║██████╔╝██║██║ █████╗ ██║ ██║ - ██║ ██║ ██║██║╚██╔╝██║██╔═══╝ ██║██║ ██╔══╝ ██║ ██║ - ╚██████╗╚██████╔╝██║ ╚═╝ ██║██║ ██║███████╗███████╗██████╔╝ - ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝╚═════╝ - - @compiled/css - Unhandled exception - - An unhandled exception was raised when parsing your CSS, this is probably a bug! - Raise an issue here: https://github.com/atlassian-labs/compiled/issues/new?assignees=&labels=&template=bug_report.md&title=CSS%20Parsing%20Exception:%20 - - Input CSS: { - red - } - - Exception: :1:1: Unknown word - " - `); + expect(actual).toThrow(); }); }); From efcd750eeea6faef4129c4ac968d48ec10e7f30f Mon Sep 17 00:00:00 2001 From: Pedro Yamada Date: Wed, 20 Mar 2024 16:05:58 +1100 Subject: [PATCH 4/5] Remove failure test --- .../src/utils/__tests__/css-builders.test.ts | 45 ------------------- 1 file changed, 45 deletions(-) diff --git a/packages/babel-plugin/src/utils/__tests__/css-builders.test.ts b/packages/babel-plugin/src/utils/__tests__/css-builders.test.ts index d73ee7179..74d032b60 100644 --- a/packages/babel-plugin/src/utils/__tests__/css-builders.test.ts +++ b/packages/babel-plugin/src/utils/__tests__/css-builders.test.ts @@ -74,49 +74,4 @@ describe('buildCss', () => { expect(css).toEqual([{ css: 'background: red;', type: 'unconditional' }]); expect(variables).toEqual([]); }); - - it('returns a css string for a variable member expression', () => { - const file = parse( - ` - import { css } from '@compiled/react'; - - const styles = { option1: css({ background: 'red' }) }; - - run(styles[key]); - `, - { sourceType: 'module' } - ); - - let path: NodePath | null = null; - traverse(file, { - CallExpression(nodePath) { - nodePath.traverse({ - MemberExpression(innerPath) { - path = innerPath; - }, - }); - }, - }); - - expect(path).not.toEqual(null); - - const meta: Metadata = { - parentPath: path!.parentPath, - state: { - cssMap: {}, - filename: '', - }, - } as any; - - const { css, variables } = buildCss(path!.node, meta); - // TODO: This should not happen - expect(css).toEqual([{ css: 'option1: var(--_g48cyt);', type: 'unconditional' }]); - expect(variables.length).toEqual(1); - expect(variables[0].name).toEqual('--_g48cyt'); - expect(generate(variables[0].expression).code).toMatchInlineSnapshot(` - "css({ - background: 'red' - })" - `); - }); }); From 885558a72e4b7fa56079441473c612d50d2b931c Mon Sep 17 00:00:00 2001 From: Pedro Yamada Date: Wed, 20 Mar 2024 16:06:36 +1100 Subject: [PATCH 5/5] Remove test enforcing bug --- .../src/__tests__/css-builder.test.ts | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/packages/babel-plugin/src/__tests__/css-builder.test.ts b/packages/babel-plugin/src/__tests__/css-builder.test.ts index 4dcbb74bf..7e8a3e147 100644 --- a/packages/babel-plugin/src/__tests__/css-builder.test.ts +++ b/packages/babel-plugin/src/__tests__/css-builder.test.ts @@ -155,24 +155,4 @@ describe('css builder', () => { " `); }); - - it('does not work when there is logic to get to the style', () => { - const actual = () => - transform(` - import { css } from '@compiled/react'; - - const styles = { - test: { - red: css({ color: 'red' }), - blue: css({ color: 'blue' }), - }, - }; - - function Component({ color }) { - return
- } - `); - - expect(actual).toThrow(); - }); });