|
| 1 | +'use strict'; |
| 2 | + |
| 3 | +require('../common'); |
| 4 | + |
| 5 | +const { |
| 6 | + generateSEA, |
| 7 | + skipIfSingleExecutableIsNotSupported, |
| 8 | +} = require('../common/sea'); |
| 9 | + |
| 10 | +skipIfSingleExecutableIsNotSupported(); |
| 11 | + |
| 12 | +// This tests the SEA VFS integration - fs.getSeaVfs() and fs.hasSeaAssets() |
| 13 | + |
| 14 | +const tmpdir = require('../common/tmpdir'); |
| 15 | +const { writeFileSync } = require('fs'); |
| 16 | +const { spawnSyncAndAssert } = require('../common/child_process'); |
| 17 | +const { join } = require('path'); |
| 18 | + |
| 19 | +const configFile = tmpdir.resolve('sea-config.json'); |
| 20 | +const seaPrepBlob = tmpdir.resolve('sea-prep.blob'); |
| 21 | +const outputFile = tmpdir.resolve(process.platform === 'win32' ? 'sea.exe' : 'sea'); |
| 22 | + |
| 23 | +// The script that will run inside the SEA |
| 24 | +const seaMain = ` |
| 25 | +'use strict'; |
| 26 | +const fs = require('fs'); |
| 27 | +const assert = require('assert'); |
| 28 | +
|
| 29 | +// Test hasSeaAssets() returns true when we have assets |
| 30 | +const hasAssets = fs.hasSeaAssets(); |
| 31 | +assert.strictEqual(hasAssets, true, 'hasSeaAssets() should return true'); |
| 32 | +console.log('hasSeaAssets:', hasAssets); |
| 33 | +
|
| 34 | +// Test getSeaVfs() returns a VFS instance |
| 35 | +const vfs = fs.getSeaVfs(); |
| 36 | +assert.ok(vfs !== null, 'getSeaVfs() should not return null'); |
| 37 | +console.log('getSeaVfs returned VFS instance'); |
| 38 | +
|
| 39 | +// Test that the VFS is mounted at /sea by default |
| 40 | +// and contains our assets |
| 41 | +
|
| 42 | +// Read the config file through standard fs (via VFS hooks) |
| 43 | +const configContent = fs.readFileSync('/sea/config.json', 'utf8'); |
| 44 | +const config = JSON.parse(configContent); |
| 45 | +assert.strictEqual(config.name, 'test-app', 'config.name should match'); |
| 46 | +assert.strictEqual(config.version, '1.0.0', 'config.version should match'); |
| 47 | +console.log('Read config.json:', config); |
| 48 | +
|
| 49 | +// Read a text file |
| 50 | +const greeting = fs.readFileSync('/sea/data/greeting.txt', 'utf8'); |
| 51 | +assert.strictEqual(greeting, 'Hello from SEA VFS!', 'greeting should match'); |
| 52 | +console.log('Read greeting.txt:', greeting); |
| 53 | +
|
| 54 | +// Test existsSync |
| 55 | +assert.strictEqual(fs.existsSync('/sea/config.json'), true); |
| 56 | +assert.strictEqual(fs.existsSync('/sea/data/greeting.txt'), true); |
| 57 | +assert.strictEqual(fs.existsSync('/sea/nonexistent.txt'), false); |
| 58 | +console.log('existsSync tests passed'); |
| 59 | +
|
| 60 | +// Test statSync |
| 61 | +const configStat = fs.statSync('/sea/config.json'); |
| 62 | +assert.strictEqual(configStat.isFile(), true); |
| 63 | +assert.strictEqual(configStat.isDirectory(), false); |
| 64 | +console.log('statSync tests passed'); |
| 65 | +
|
| 66 | +// Test readdirSync |
| 67 | +const entries = fs.readdirSync('/sea'); |
| 68 | +assert.ok(entries.includes('config.json'), 'Should include config.json'); |
| 69 | +assert.ok(entries.includes('data'), 'Should include data directory'); |
| 70 | +console.log('readdirSync tests passed, entries:', entries); |
| 71 | +
|
| 72 | +// Test requiring a module from SEA VFS |
| 73 | +const mathModule = require('/sea/modules/math.js'); |
| 74 | +assert.strictEqual(mathModule.add(2, 3), 5, 'math.add should work'); |
| 75 | +assert.strictEqual(mathModule.multiply(4, 5), 20, 'math.multiply should work'); |
| 76 | +console.log('require from VFS tests passed'); |
| 77 | +
|
| 78 | +// Test getSeaVfs with custom prefix |
| 79 | +const customVfs = fs.getSeaVfs({ prefix: '/custom' }); |
| 80 | +// Note: getSeaVfs is a singleton, so it returns the same instance |
| 81 | +// with the same mount point (/sea) regardless of options passed after first call |
| 82 | +assert.strictEqual(customVfs, vfs, 'Should return the same cached instance'); |
| 83 | +console.log('Cached VFS instance test passed'); |
| 84 | +
|
| 85 | +console.log('All SEA VFS tests passed!'); |
| 86 | +`; |
| 87 | + |
| 88 | +// Create the main script file |
| 89 | +writeFileSync(tmpdir.resolve('sea-main.js'), seaMain); |
| 90 | + |
| 91 | +// Create asset files |
| 92 | +writeFileSync(tmpdir.resolve('config.json'), JSON.stringify({ |
| 93 | + name: 'test-app', |
| 94 | + version: '1.0.0', |
| 95 | +})); |
| 96 | + |
| 97 | +writeFileSync(tmpdir.resolve('greeting.txt'), 'Hello from SEA VFS!'); |
| 98 | + |
| 99 | +writeFileSync(tmpdir.resolve('math.js'), ` |
| 100 | +module.exports = { |
| 101 | + add: (a, b) => a + b, |
| 102 | + multiply: (a, b) => a * b, |
| 103 | +}; |
| 104 | +`); |
| 105 | + |
| 106 | +// Create SEA config with assets |
| 107 | +writeFileSync(configFile, JSON.stringify({ |
| 108 | + main: 'sea-main.js', |
| 109 | + output: 'sea-prep.blob', |
| 110 | + assets: { |
| 111 | + 'config.json': 'config.json', |
| 112 | + 'data/greeting.txt': 'greeting.txt', |
| 113 | + 'modules/math.js': 'math.js', |
| 114 | + }, |
| 115 | +})); |
| 116 | + |
| 117 | +// Generate the SEA prep blob |
| 118 | +spawnSyncAndAssert( |
| 119 | + process.execPath, |
| 120 | + ['--experimental-sea-config', 'sea-config.json'], |
| 121 | + { cwd: tmpdir.path }, |
| 122 | + {} |
| 123 | +); |
| 124 | + |
| 125 | +// Generate the SEA executable |
| 126 | +generateSEA(outputFile, process.execPath, seaPrepBlob); |
| 127 | + |
| 128 | +// Run the SEA and verify output |
| 129 | +spawnSyncAndAssert( |
| 130 | + outputFile, |
| 131 | + [], |
| 132 | + { |
| 133 | + env: { |
| 134 | + ...process.env, |
| 135 | + NODE_DEBUG_NATIVE: undefined, |
| 136 | + }, |
| 137 | + }, |
| 138 | + { |
| 139 | + stdout: /All SEA VFS tests passed!/, |
| 140 | + } |
| 141 | +); |
0 commit comments