From d22fb038007c04747cd9cdf14ed80d1ae7686fb6 Mon Sep 17 00:00:00 2001 From: reaper Date: Sat, 1 Nov 2025 20:21:50 +0530 Subject: [PATCH] test: add comprehensive tests for island functionality --- playground/server.js | 4 +- pnpm-lock.yaml | 46 +++---- tests/comprehensive.js | 281 +++++++++++++++++++++++++++++++++++++++++ tests/integration.js | 85 +++++++++++++ 4 files changed, 391 insertions(+), 25 deletions(-) create mode 100644 tests/comprehensive.js create mode 100644 tests/integration.js diff --git a/playground/server.js b/playground/server.js index 8ea7f64..a7f940b 100644 --- a/playground/server.js +++ b/playground/server.js @@ -1,5 +1,5 @@ import express from 'express' -import comToString from 'preact-render-to-string' +import renderToString from 'preact-render-to-string' import serveStatic from 'serve-static' import { join } from 'path' import Container from './Container' @@ -9,7 +9,7 @@ const app = express() app.use('/public', serveStatic(join(__dirname, './client'))) app.get('/', (req, res) => { - return res.send(comToString()) + return res.send(renderToString()) }) app.listen(3000, () => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b83ff00..89f68c0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -318,133 +318,133 @@ packages: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} '@esbuild/android-arm64@0.18.20': - resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==, tarball: https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz} engines: {node: '>=12'} cpu: [arm64] os: [android] '@esbuild/android-arm@0.18.20': - resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==, tarball: https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz} engines: {node: '>=12'} cpu: [arm] os: [android] '@esbuild/android-x64@0.18.20': - resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==, tarball: https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz} engines: {node: '>=12'} cpu: [x64] os: [android] '@esbuild/darwin-arm64@0.18.20': - resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==, tarball: https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz} engines: {node: '>=12'} cpu: [arm64] os: [darwin] '@esbuild/darwin-x64@0.18.20': - resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==, tarball: https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz} engines: {node: '>=12'} cpu: [x64] os: [darwin] '@esbuild/freebsd-arm64@0.18.20': - resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==, tarball: https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] '@esbuild/freebsd-x64@0.18.20': - resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==, tarball: https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz} engines: {node: '>=12'} cpu: [x64] os: [freebsd] '@esbuild/linux-arm64@0.18.20': - resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==, tarball: https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz} engines: {node: '>=12'} cpu: [arm64] os: [linux] '@esbuild/linux-arm@0.18.20': - resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==, tarball: https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz} engines: {node: '>=12'} cpu: [arm] os: [linux] '@esbuild/linux-ia32@0.18.20': - resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==, tarball: https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz} engines: {node: '>=12'} cpu: [ia32] os: [linux] '@esbuild/linux-loong64@0.18.20': - resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==, tarball: https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz} engines: {node: '>=12'} cpu: [loong64] os: [linux] '@esbuild/linux-mips64el@0.18.20': - resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==, tarball: https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz} engines: {node: '>=12'} cpu: [mips64el] os: [linux] '@esbuild/linux-ppc64@0.18.20': - resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==, tarball: https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz} engines: {node: '>=12'} cpu: [ppc64] os: [linux] '@esbuild/linux-riscv64@0.18.20': - resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==, tarball: https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz} engines: {node: '>=12'} cpu: [riscv64] os: [linux] '@esbuild/linux-s390x@0.18.20': - resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==, tarball: https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz} engines: {node: '>=12'} cpu: [s390x] os: [linux] '@esbuild/linux-x64@0.18.20': - resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==, tarball: https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz} engines: {node: '>=12'} cpu: [x64] os: [linux] '@esbuild/netbsd-x64@0.18.20': - resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==, tarball: https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz} engines: {node: '>=12'} cpu: [x64] os: [netbsd] '@esbuild/openbsd-x64@0.18.20': - resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==, tarball: https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz} engines: {node: '>=12'} cpu: [x64] os: [openbsd] '@esbuild/sunos-x64@0.18.20': - resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==, tarball: https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz} engines: {node: '>=12'} cpu: [x64] os: [sunos] '@esbuild/win32-arm64@0.18.20': - resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==, tarball: https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz} engines: {node: '>=12'} cpu: [arm64] os: [win32] '@esbuild/win32-ia32@0.18.20': - resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==, tarball: https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz} engines: {node: '>=12'} cpu: [ia32] os: [win32] '@esbuild/win32-x64@0.18.20': - resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==, tarball: https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz} engines: {node: '>=12'} cpu: [x64] os: [win32] @@ -998,7 +998,7 @@ packages: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, tarball: https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] diff --git a/tests/comprehensive.js b/tests/comprehensive.js new file mode 100644 index 0000000..de44d39 --- /dev/null +++ b/tests/comprehensive.js @@ -0,0 +1,281 @@ +const { test } = require('uvu') +const { inlineSnapshot } = require('uvu-inline-snapshot') +const { generateIslandsWithSource } = require('../lib/plugin') + +test('should handle island with arrow function component', async () => { + const output = generateIslandsWithSource( + ` + // @island + const Component = () => { + return

hello

+ } + + export default Component + `, + './index.jsx', + { + rootDir: '.', + client: { + output: '.islands', + }, + } + ) + + await inlineSnapshot( + output.code.server, + `import {Fragment} from "preact"; +import {h} from "preact"; +const Component = () => { + return

hello

; +}; +export default Component; +export default function IslandComponent(props) { + return h(Fragment, {}, h("island-component", { + "data-props": JSON.stringify(props) + }, h(Component, props))); +} +` + ) +}) + +test('should handle island with lazy loading', async () => { + const output = generateIslandsWithSource( + ` + // @island lazy + export default function Component(){ + return

hello

+ } + `, + './index.jsx', + { + rootDir: '.', + client: { + output: '.islands', + }, + } + ) + + // Check that the client code includes lazy loading logic + const clientCode = output.code.client + if (!clientCode.includes('renderOnView')) { + throw new Error('Lazy loading not implemented in client code') + } + + if (!clientCode.includes('IntersectionObserver')) { + throw new Error('IntersectionObserver not used for lazy loading') + } +}) + +test('should handle island with custom lazy threshold', async () => { + const output = generateIslandsWithSource( + ` + // @island lazy:0.8 + export default function Component(){ + return

hello

+ } + `, + './index.jsx', + { + rootDir: '.', + client: { + output: '.islands', + }, + } + ) + + // Check that the threshold is set correctly + if (!output.code.client.includes('threshold:0.8')) { + throw new Error('Custom threshold not applied') + } +}) + +test('should handle atomic mode', async () => { + const output = generateIslandsWithSource( + ` + // @island + export default function Component(){ + return

hello

+ } + `, + './index.jsx', + { + rootDir: '.', + baseURL: '/public', + atomic: true, + client: { + output: '.islands', + }, + } + ) + + // In atomic mode, should include script tag + if (!output.code.server.includes('h("script"')) { + throw new Error('Atomic mode should include script tag') + } +}) + +test('should handle non-atomic mode', async () => { + const output = generateIslandsWithSource( + ` + // @island + export default function Component(){ + return

hello

+ } + `, + './index.jsx', + { + rootDir: '.', + baseURL: '/public', + atomic: false, + client: { + output: '.islands', + }, + } + ) + + // In non-atomic mode, should not include script tag + if (output.code.server.includes(' { + const output = generateIslandsWithSource( + ` + // @island + export default function Component(){ + return

hello

+ } + `, + './index.jsx', + { + rootDir: '.', + hash: true, + client: { + output: '.islands', + }, + } + ) + + // Should have hash in the client path + if (!output.paths.client.includes('-')) { + throw new Error('Hash not applied to client path') + } +}) + +test('should handle replaceParentNode option', async () => { + const output = generateIslandsWithSource( + ` + // @island + export default function Component(){ + return

hello

+ } + `, + './index.jsx', + { + rootDir: '.', + client: { + output: '.islands', + replaceParentNode: true, + }, + } + ) + + // Should pass replaceParentNode to render call + if ( + !output.code.client.includes( + 'render(restoreTree(this.component.default, this.baseProps), this, this)' + ) + ) { + throw new Error('replaceParentNode not applied correctly') + } +}) + +test('should detect island by filename pattern', async () => { + const output = generateIslandsWithSource( + ` + export default function Component(){ + return

hello

+ } + `, + './Component.island.jsx', + { + rootDir: '.', + client: { + output: '.islands', + }, + } + ) + + // Should detect as island based on filename + if (output.code.server === output.code.client) { + throw new Error('Island not detected by filename pattern') + } +}) + +test('should handle component with event handlers', async () => { + const output = generateIslandsWithSource( + ` + export default function Component(){ + const handleClick = () => {} + return + } + `, + './index.jsx', + { + rootDir: '.', + client: { + output: '.islands', + }, + } + ) + + // Should detect as island due to event handler + if (output.code.server === output.code.client) { + throw new Error('Island not detected by event handler') + } +}) + +test('should handle component with internal function', async () => { + const output = generateIslandsWithSource( + ` + export default function Component(){ + const internalFunc = () => {} + return + } + `, + './index.jsx', + { + rootDir: '.', + client: { + output: '.islands', + }, + } + ) + + // Should detect as island due to internal function used in JSX + if (output.code.server === output.code.client) { + throw new Error('Island not detected by internal function in JSX') + } +}) + +test('should not detect island for component with only data attributes', async () => { + const code = ` + export default function Component(){ + return
hello
+ } + ` + const output = generateIslandsWithSource(code, './index.jsx', { + rootDir: '.', + client: { + output: '.islands', + }, + }) + + // Should not detect as island - only data attributes + if (output.code.server !== code) { + throw new Error('Incorrectly detected island for data attributes only') + } +}) + +test.run() diff --git a/tests/integration.js b/tests/integration.js new file mode 100644 index 0000000..8c56b0b --- /dev/null +++ b/tests/integration.js @@ -0,0 +1,85 @@ +const { test } = require('uvu') +const { execSync, spawn } = require('child_process') +const { readFileSync, existsSync } = require('fs') +const { join } = require('path') + +test('integration - playground builds successfully', async () => { + // Change to playground directory + process.chdir(join(__dirname, '../playground')) + + try { + // Install dependencies + execSync('pnpm install', { stdio: 'inherit' }) + + // Build with esbuild + execSync('npm run build:esbuild', { stdio: 'inherit' }) + + // Check that client files were generated + const clientDir = join(__dirname, '../playground/dist/client') + if (!existsSync(clientDir)) { + throw new Error('Client directory not created') + } + + // Check that island files exist + const counterIsland = join(clientDir, 'Counter.js') + const counterTSIsland = join(clientDir, 'CounterTS.js') + + if (!existsSync(counterIsland)) { + throw new Error('Counter island not generated') + } + + if (!existsSync(counterTSIsland)) { + throw new Error('CounterTS island not generated') + } + + // Verify the generated client code contains expected content + const counterCode = readFileSync(counterIsland, 'utf8') + if (!counterCode.includes('customElements.define("island-counter"')) { + throw new Error('Counter island client code is malformed') + } + + const counterTSCcode = readFileSync(counterTSIsland, 'utf8') + if ( + !counterTSCcode.includes('customElements.define("island-counter-t-s"') + ) { + throw new Error('CounterTS island client code is malformed') + } + + console.log('✓ Playground integration test passed') + } finally { + // Change back to root directory + process.chdir(join(__dirname, '..')) + } +}) + +test('integration - simple-express example builds successfully', async () => { + // Change to example directory + process.chdir(join(__dirname, '../examples/simple-express')) + + try { + // Install dependencies + execSync('pnpm install', { stdio: 'inherit' }) + + // Build the example + execSync('npm run build', { stdio: 'inherit' }) + + // Check that dist directory was created + const distDir = join(__dirname, '../examples/simple-express/dist') + if (!existsSync(distDir)) { + throw new Error('Dist directory not created') + } + + // Check that app.js exists + const appFile = join(distDir, 'app.js') + if (!existsSync(appFile)) { + throw new Error('App file not built') + } + + console.log('✓ Simple-express integration test passed') + } finally { + // Change back to root directory + process.chdir(join(__dirname, '..')) + } +}) + +test.run()