Closed GrayedFox closed 4 months ago
Hi, so the first thing you propably don't need is this part: additionalEntryPoints: ['src/app.js', 'src/sass/agentSignatures/global.scss', 'src/sass/global.scss'], perhaps for the scss-files, but propably not the app.js this prop is for parts of your application that are compiled into separate bundles and imported at runtime.
Everything else seems pretty straighforward. For anything more than this I'd need the console.log of the browser window in which the test is running and perhaps the log of the esbuild-server.
As a general rule of thumb I'd recommend first having a working esbuild-setup for the app and then applying it to the cypress-devserver. If that is not possible you could also start with a simplified config create a test-component that doesn't import anything but just renders some basic html:
import React from 'react'
export const TestComponent = () => (
<h1>Hello World</h1>
)
then you can start with a much simplified config:
const { defineConfig } = require('cypress');
const { createEsbuildDevServer } = require('cypress-devserver-esbuild');
const path = require('node:path');
const esBuildConfig = {
loader: {
'.js': 'jsx'
},
publicPath: 'dist/assets/',
outdir: 'dist/',
assetNames: '[name].[ext]',
sourcemap: true,
bundle: true,
plugins: []
};
const esServerLog = (level = 6, messages) => {};
module.exports = defineConfig({
viewportWidth: 1280,
viewportHeight: 800,
projectId: '7yiz8w',
chromeWebSecurity: false,
videoCompression: 15,
retries: {
runMode: 2,
openMode: 0
},
scrollBehavior: 'center',
defaultCommandTimeout: 10_000,
component: {
specPattern: 'cypress/component/**/*.spec.{ts,tsx}',
supportFile: 'cypress/support/component.tsx',
devServer: createEsbuildDevServer(esBuildConfig, {
additionalEntryPoints: ['src/app.js', 'src/sass/agentSignatures/global.scss', 'src/sass/global.scss'],
singleBundle: false,
port: 6789,
logFunction: esServerLog
})
},
Once this is running start adding stuff to your test-component and get it running step by step. :)
Thanks for the tips that helped me figure out a way forward. Noticed a few things which might help future eyeballs, also added a bunch of extra log(6, ...)
calls to both this project and your other one to help with debugging - can open some PRs if you think the extra output would be helpful but it's just logging at the moment!
That said I'm really happy with the performance and functionality so far, if we end up forking and fixing anything here we'll make sure to keep the MIT in (as legally required) but also list you and this repo in our thanks/acknowledgements section and I'll make some PRs pointing back here 🩵
Here's a summary of what I worked through.
using the publicPath
setting caused two extra folders _.._/cypress
folder to be prefixed to the build path, i.e. it became _.._/cypress/dist/assets
which the devserver didn't pick up, breaking the express app redirect. Removing the publicPath
var from esbuild fixed this.
typescript was behaving badly, despite the esbuild picking up on the base tsconfig (which is good) the internal tsc was incorrectly parsing the entire project when we just want the component's under test to be parsed in this case (work around below)
the minimatcher section in the underlying repo wasn't expanding our spec pattern so I decided to do that ahead of time, which fixed the mapping and resolved the 'Non test file found with no match` (or similar) error
We have the following project structure:
cypress/
- component/
- e2e/
- support/
- tsconfig.json
src/
- ...
dist/
- esbulid/
- ...
cypress.config.js
tsconfig.json
Here's some sample code that might help people along:
// config
const supportFile = path.resolve(__dirname, './cypress/support/component.tsx');
const tsConfigJson = JSON.parse(readFileSync('./cypress/tsconfig.json'));
/**
* The way the dev-server and esbuild interact with typescript is tricky. The cy/tsconfig settings
* are correct and make the IDE (tsc) provide sane type hinting and module resolution for writing
* component tests in typescript, but these settings are _incorrect_ for bundling our app (i.e.
* esnext vs es5 targets, alias paths, etc). Running tests from the root dir correctly picks up
* the root tscfonfig file and settings, which esbuild respects, but this results in the auto
* bundling by the dev-server expand the [*.ts, *.tsx] globs from the cy/tsconfig from the root dir
* which sends both esbuild and the dev server to wonky town. Changing the baseUrl breaks esbuild so
* that's also out.
*
* To get the best of both worlds and ensure that errors reported by the dev-server are accurate we:
* 1. use typescript's internal config parser to resolve relative paths correctly
* 2. prefix the local tsconfig include paths with `cypress/`
* 3. pluck out the fully resolved paths of our component specs which we feed to the cypress
* which the dev-server inherits instead of using a direct spec pattern
*
* The first step pre-empts the build process and parses the cy/tsconfig with the correct base path.
* The second step muzzles tsc's false positives and nets us a nice performance gain, as the resolved
* files tsc cares about when we run tests shouldn't be the entire project, but just our component
* tests (reduces resolved files from entire proj to just cypress folder matches). The third step
* works around a bug that the esbuild-dev-server package has with it's bundle mapping logic and
* ensures bundles are correctly mapped to the matching component spec file.
**/
// prefix include entries with 'cypress/' pattern to prevent root dir glob expansion into entire project
tsConfigJson.include = tsConfigJson.include.map(entry => `cypress/${entry}`);
// parse tsConfig using same method typescript compiler does
const tsConfigParsed = parseJsonConfigFileContent(tsConfigJson, tsSys, path.dirname('./cypress/'));
// use fileNames in parsed tsConfig to pluck out resolved spec paths
const componentSpecs = tsConfigParsed.fileNames.filter(entry => entry.includes('spec'));
// get esbuild config and add component test specific settings
const esBuildConfig = {
...getEsBuildConfig(),
outdir: 'dist/esbuild/'
};
console.log({
esBuildConfig,
supportFile,
componentSpecs
});
module.exports = defineConfig({
...
component: {
specPattern: componentSpecs,
supportFile,
devServer: createEsbuildDevServer(esBuildConfig, {
singleBundle: false,
port: 3993,
logFunction: (level = 6, messages) => {
console.log(messages);
}
})
}
Reloading a component under test seems to break things but I will create a new issue for that ⚡
Hi, I wasn't aware of the public path issue, as we handle that via an external proxy so I did not need that yet. I guess we can simply drop that setting when parsing the config and adding a note to the readme. If you have some additional logging or changes that are required, feel free to open PR's and I'll review/merge them in a timely manner (usually 1-2 days at most).
This lib uses globby to resolve the spec-paths, so any globby-compatible pattern should work. potentially something like 'cypress/component/*/.spec.ts*' instead. See https://github.com/sindresorhus/globby?tab=readme-ov-file#globbing-patterns I can add that to the docs
Ahoy there, gave this cool little a project a red hot go today but despite resolving all esbuild errors (main app code uses webpack) I couldn't get the devserver to ever render the component under test.
Apologies for the rude config dump but maybe you see something I don't? 🖖🏽
The component I'm trying to mount is a pretty basic React JSX component:
Test file is literally just an
it()
block that usescy.mount()
to try and mount the component. I have a feeling the problem lies with me sort of piggy backing the dev-server build config to try and bundle our application code, but your approach (with the custom tiny express server underneath) was pretty much how I thought I would tackle this too.If there's anything that sings out let me know and thanks for laying the foundations ⚡