From f50fd4d65941820b50c13d8f915feb6feac18de5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AD=99=E4=B8=80=E5=86=B0?= <14512567+sunyibing@user.noreply.gitee.com> Date: Wed, 7 Aug 2024 17:25:12 +0800 Subject: [PATCH] fix: fix template literal state compute bug and add tests --- packages/inula-next/test/computed.test.tsx | 548 ++++++++++++++++++ packages/inula-next/test/state.test.tsx | 351 ++++++++++- .../src/analyze/utils.ts | 6 +- 3 files changed, 894 insertions(+), 11 deletions(-) diff --git a/packages/inula-next/test/computed.test.tsx b/packages/inula-next/test/computed.test.tsx index 793a6766..8e229e81 100644 --- a/packages/inula-next/test/computed.test.tsx +++ b/packages/inula-next/test/computed.test.tsx @@ -87,6 +87,195 @@ describe('Computed Properties', () => { container.querySelector('button')!.click(); expect(resultElement.textContent).toBe('60'); }); + + it('Should correctly compute and render a derived string state', ({ container }) => { + let resultElement: HTMLElement; + + function App() { + let firstName = 'John'; + let lastName = 'Doe'; + const fullName = `${firstName} ${lastName}`; + + didMount(() => { + resultElement = container.querySelector('[data-testid="result"]')!; + }); + + function updateName() { + firstName = 'Jane'; + } + + return ( +
{fullName}
+ +Total: ${total}
+ +Is Adult: {isAdult ? 'Yes' : 'No'}
+ +Even numbers: {evenNumbers.join(', ')}
+ ++ {userSummary.name} is {userSummary.isAdult ? 'an adult' : 'not an adult'} +
+ +Current item: {currentItem}
+ +{fullName}
+ +Volume: {volume}
+ +Can Drive: {canDrive ? 'Yes' : 'No'}
+ +Filtered numbers: {result.join(', ')}
+ ++ {userProfile.name} ({userProfile.age}) - {userProfile.status} - Theme: {userProfile.theme} +
+ +Sum: {sum}
+Difference: {difference}
+Product: {product}
+Quotient: {quotient}
+Sum: 15
Difference: 5
Product: 50
Quotient: 2
'); + }); + + it('Should support array indexing', ({ container }) => { + let resultElement: HTMLElement; + + function App() { + let arr = [10, 20, 30, 40, 50]; + let index = 2; + const value = arr[index]; + + didMount(() => { + resultElement = container.querySelector('[data-testid="result"]')!; + }); + + function updateIndex() { + index = 4; + } + + return ( +Value: {value}
+ +Name: {name}
+ +Sum: {sum}
; + } + + render(App, container); + + expect(resultElement.textContent).toBe('Sum: 15'); + }); + + it('Should support various number operations', ({ container }) => { + let resultElement: HTMLElement; + + function App() { + let num = 3.14159; + const rounded = Math.round(num); + const floored = Math.floor(num); + const ceiled = Math.ceil(num); + const squared = Math.pow(num, 2); + + didMount(() => { + resultElement = container.querySelector('[data-testid="result"]')!; + }); + + return ( +Rounded: {rounded}
+Floored: {floored}
+Ceiled: {ceiled}
+Squared: {squared.toFixed(2)}
+Rounded: 3
Floored: 3
Ceiled: 4
Squared: 9.87
'); + }); + + it('Should support map operations', ({ container }) => { + let resultElement: HTMLElement; + + function App() { + let numbers = [1, 2, 3, 4, 5]; + const squaredNumbers = numbers.map(n => n * n); + + didMount(() => { + resultElement = container.querySelector('[data-testid="result"]')!; + }); + + returnSquared: {squaredNumbers.join(', ')}
; + } + + render(App, container); + + expect(resultElement.textContent).toBe('Squared: 1, 4, 9, 16, 25'); + }); + + it('Should support conditional expressions', ({ container }) => { + let resultElement: HTMLElement; + + function App() { + let age = 20; + let hasLicense = true; + const canDrive = age >= 18 ? (hasLicense ? 'Yes' : 'No, needs license') : 'No, too young'; + + didMount(() => { + resultElement = container.querySelector('[data-testid="result"]')!; + }); + + function updateAge() { + age = 16; + } + + return ( +Can Drive: {canDrive}
+ +{str}
; + } + render(StringState, container); + expect(container.innerHTML).toBe('Hello, World!
'); + }); + + it('Should correctly declare and render a number state variable', ({ container }) => { + function NumberState() { + let num = 42; + return{num}
; + } + render(NumberState, container); + expect(container.innerHTML).toBe('42
'); + }); + + it('Should correctly declare and render a boolean state variable', ({ container }) => { + function BooleanState() { + let bool = true; + return{bool.toString()}
; + } + render(BooleanState, container); + expect(container.innerHTML).toBe('true
'); + }); + + it('Should correctly declare and render an array state variable', ({ container }) => { + function ArrayState() { + let arr = [1, 2, 3]; + return{arr.join(', ')}
; + } + render(ArrayState, container); + expect(container.innerHTML).toBe('1, 2, 3
'); + }); + + it('Should correctly declare and render an object state variable', ({ container }) => { + function ObjectState() { + let obj = { name: 'John', age: 30 }; + return{`${obj.name}, ${obj.age}`}
; + } + render(ObjectState, container); + expect(container.innerHTML).toBe('John, 30
'); + }); + + it('Should correctly declare and render a map state variable', ({ container }) => { + function MapState() { + let map = new Map([ + ['key1', 'value1'], + ['key2', 'value2'], + ]); + return ( ++ {Array.from(map) + .map(([k, v]) => `${k}:${v}`) + .join(', ')} +
+ ); + } + render(MapState, container); + expect(container.innerHTML).toBe('key1:value1, key2:value2
'); + }); + + it('Should correctly declare and render a set state variable', ({ container }) => { + function SetState() { + let set = new Set([1, 2, 3]); + return{Array.from(set).join(', ')}
; + } + render(SetState, container); + expect(container.innerHTML).toBe('1, 2, 3
'); + }); +}); + +describe('Update state', () => { + it('Should correctly update and render a string state variable', ({ container }) => { + let updateStr: () => void; + function StringState() { + let str = 'Hello'; + updateStr = () => { + str = 'Hello, World!'; + }; + return{str}
; + } + render(StringState, container); + expect(container.innerHTML).toBe('Hello
'); + updateStr(); + expect(container.innerHTML).toBe('Hello, World!
'); + }); + + it('Should correctly update and render a number state variable', ({ container }) => { + let updateNum: () => void; + function NumberState() { + let num = 42; + updateNum = () => { + num = 84; + }; + return{num}
; + } + render(NumberState, container); + expect(container.innerHTML).toBe('42
'); + updateNum(); + expect(container.innerHTML).toBe('84
'); + }); + + it('Should correctly update and render a boolean state variable', ({ container }) => { + let toggleBool: () => void; + function BooleanState() { + let bool = true; + toggleBool = () => { + bool = !bool; + }; + return{bool.toString()}
; + } + render(BooleanState, container); + expect(container.innerHTML).toBe('true
'); + toggleBool(); + expect(container.innerHTML).toBe('false
'); + }); + + it('Should correctly update and render an object state variable', ({ container }) => { + let updateObj: () => void; + function ObjectState() { + let obj = { name: 'John', age: 30 }; + updateObj = () => { + obj.age = 31; + }; + return{`${obj.name}, ${obj.age}`}
; + } + render(ObjectState, container); + expect(container.innerHTML).toBe('John, 30
'); + updateObj(); + expect(container.innerHTML).toBe('John, 31
'); + }); + + it('Should correctly handle increment operations (n++)', ({ container }) => { + let increment: () => void; + function IncrementState() { let count = 0; - incrementCount = () => { + increment = () => { count++; }; + return{count}
; + } + render(IncrementState, container); + expect(container.innerHTML).toBe('0
'); + increment(); + expect(container.innerHTML).toBe('1
'); + }); - return{count}
; + } + render(DecrementState, container); + expect(container.innerHTML).toBe('5
'); + decrement(); + expect(container.innerHTML).toBe('4
'); + }); + + it('Should correctly handle operations (+=)', ({ container }) => { + let addValue: (value: number) => void; + function AdditionAssignmentState() { + let count = 10; + addValue = (value: number) => { + count += value; + }; + return{count}
; + } + render(AdditionAssignmentState, container); + expect(container.innerHTML).toBe('10
'); + addValue(5); + expect(container.innerHTML).toBe('15
'); + addValue(-3); + expect(container.innerHTML).toBe('12
'); + }); + + it('Should correctly handle operations (-=)', ({ container }) => { + let subtractValue: (value: number) => void; + function SubtractionAssignmentState() { + let count = 20; + subtractValue = (value: number) => { + count -= value; + }; + return{count}
; + } + render(SubtractionAssignmentState, container); + expect(container.innerHTML).toBe('20
'); + subtractValue(7); + expect(container.innerHTML).toBe('13
'); + subtractValue(-4); + expect(container.innerHTML).toBe('17
'); + }); + + it('Should correctly update and render a state variable as an index of array', ({ container }) => { + let updateIndex: () => void; + function ArrayIndexState() { + const items = ['Apple', 'Banana', 'Cherry', 'Date']; + let index = 0; + updateIndex = () => { + index = (index + 1) % items.length; + }; + return{items[index]}
; + } + render(ArrayIndexState, container); + expect(container.innerHTML).toBe('Apple
'); + updateIndex(); + expect(container.innerHTML).toBe('Banana
'); + updateIndex(); + expect(container.innerHTML).toBe('Cherry
'); + updateIndex(); + expect(container.innerHTML).toBe('Date
'); + updateIndex(); + expect(container.innerHTML).toBe('Apple
'); + }); + + it('Should correctly update and render a state variable as a property of an object', ({ container }) => { + let updatePerson: () => void; + function ObjectPropertyState() { + let person = { name: 'Alice', age: 30, job: 'Engineer' }; + updatePerson = () => { + person.age += 1; + person.job = 'Senior Engineer'; + }; + return ( +Name: {person.name}
+Age: {person.age}
+Job: {person.job}
+Name: Alice
Age: 30
Job: Engineer
Name: Alice
Age: 31
Job: Senior Engineer
{items.join(', ')}
; + } + render(ArrayPushState, container); + expect(container.innerHTML).toBe('Apple, Banana
'); + pushItem(); + expect(container.innerHTML).toBe('Apple, Banana, Cherry
'); + }); + + it('Should correctly update and render an array state variable - pop operation', ({ container }) => { + let popItem: () => void; + function ArrayPopState() { + let items = ['Apple', 'Banana', 'Cherry']; + popItem = () => { + items.pop(); + }; + return{items.join(', ')}
; + } + render(ArrayPopState, container); + expect(container.innerHTML).toBe('Apple, Banana, Cherry
'); + popItem(); + expect(container.innerHTML).toBe('Apple, Banana
'); + }); + + it('Should correctly update and render an array state variable - unshift operation', ({ container }) => { + let unshiftItem: () => void; + function ArrayUnshiftState() { + let items = ['Banana', 'Cherry']; + unshiftItem = () => { + items.unshift('Apple'); + }; + return{items.join(', ')}
; + } + render(ArrayUnshiftState, container); + expect(container.innerHTML).toBe('Banana, Cherry
'); + unshiftItem(); + expect(container.innerHTML).toBe('Apple, Banana, Cherry
'); + }); + + it('Should correctly update and render an array state variable - shift operation', ({ container }) => { + let shiftItem: () => void; + function ArrayShiftState() { + let items = ['Apple', 'Banana', 'Cherry']; + shiftItem = () => { + items.shift(); + }; + return{items.join(', ')}
; + } + render(ArrayShiftState, container); + expect(container.innerHTML).toBe('Apple, Banana, Cherry
'); + shiftItem(); + expect(container.innerHTML).toBe('Banana, Cherry
'); + }); + + it('Should correctly update and render an array state variable - splice operation', ({ container }) => { + let spliceItems: () => void; + function ArraySpliceState() { + let items = ['Apple', 'Banana', 'Cherry', 'Date']; + spliceItems = () => { + items.splice(1, 2, 'Elderberry', 'Fig'); + }; + return{items.join(', ')}
; + } + render(ArraySpliceState, container); + expect(container.innerHTML).toBe('Apple, Banana, Cherry, Date
'); + spliceItems(); + expect(container.innerHTML).toBe('Apple, Elderberry, Fig, Date
'); + }); + + it('Should correctly update and render an array state variable - filter operation', ({ container }) => { + let filterItems: () => void; + function ArrayFilterState() { + let items = ['Apple', 'Banana', 'Cherry', 'Date']; + filterItems = () => { + items = items.filter(item => item.length > 5); + }; + return{items.join(', ')}
; + } + render(ArrayFilterState, container); + expect(container.innerHTML).toBe('Apple, Banana, Cherry, Date
'); + filterItems(); + expect(container.innerHTML).toBe('Banana, Cherry
'); + }); + + it('Should correctly update and render an array state variable - map operation', ({ container }) => { + let mapItems: () => void; + function ArrayMapState() { + let items = ['apple', 'banana', 'cherry']; + mapItems = () => { + items = items.map(item => item.toUpperCase()); + }; + return{items.join(', ')}
; } - render(UserInput, container); - expect(container.innerHTML).toBe('apple, banana, cherry
'); + mapItems(); + expect(container.innerHTML).toBe('APPLE, BANANA, CHERRY
'); }); }); diff --git a/packages/transpiler/babel-inula-next-core/src/analyze/utils.ts b/packages/transpiler/babel-inula-next-core/src/analyze/utils.ts index 92d76574..2918694c 100644 --- a/packages/transpiler/babel-inula-next-core/src/analyze/utils.ts +++ b/packages/transpiler/babel-inula-next-core/src/analyze/utils.ts @@ -58,7 +58,11 @@ export function extractFnBody(node: t.FunctionExpression | t.ArrowFunctionExpres } export function isStaticValue(node: t.VariableDeclarator['init']) { - return t.isLiteral(node) || t.isArrowFunctionExpression(node) || t.isFunctionExpression(node); + return ( + (t.isLiteral(node) && !t.isTemplateLiteral(node)) || + t.isArrowFunctionExpression(node) || + t.isFunctionExpression(node) + ); } export function assertComponentNode(node: any): asserts node is ComponentNode { -- Gitee