Summer Sale! All accounts are 50% off this week.

bzarboni's avatar

Cypress 10 Component tests with Ziggy

Hi everyone, as the title implies, I'm struggling to get component testing working with my Laravel app (React front-end), using Ziggy. I've tried messing around with webpack config and babel config, to no luck.

The issue is that my JSX uses the route function from Ziggy, which I think some magic happens in the background (on build?) so that those references resolve. The problem is that those aren't resolved when Cypress attempts to mount my component, and I get:

click (uncaught exception)ReferenceError: route is not defined

Anyone have success in getting component tests to work in Cypress?

For reference:

[cypress.config.js]

const { defineConfig } = require('cypress');
const webpackConfig = require('./cypress/webpack.cypress.config');

module.exports = defineConfig({
    modifyObstructiveCode: true,
    experimentalSourceRewriting: true,
    chromeWebSecurity: false,
    component: {
        devServer: {
            framework: 'react',
            bundler: 'webpack',
            webpackConfig: webpackConfig,
        },
        supportFile: 'cypress/support/component.js',
        specPattern: 'cypress/**/**/*.cy.{js,ts,jsx,tsx}',
        setupNodeEvents(on, _) {
            on('file:preprocessor', () => {
                on(
                    'file:preprocessor',
                    require('@cypress/code-coverage/use-babelrc')
                );
            });
        },
    },
});

and [webpack.cypress.config.js]:

const path = require('path');
const webpack = require('webpack');

module.exports = {
    mode: 'development',
    devtool: false,
    resolve: {
        alias: {
            '@': path.resolve('resources/js'),
        },
        modules: [path.join(__dirname, 'modules'), 'node_modules'],
    },
    plugins: [
        new webpack.ProvidePlugin({
            React: 'react',
        }),
    ],
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: [/node_modules/],
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: ['@babel/preset-env', '@babel/preset-react'],
                        plugins: [
                            [
                                '@babel/plugin-transform-modules-commonjs',
                                {
                                    loose: true,
                                },
                            ],
                        ],
                    },
                },
            },
            // For compiling sass
            {
                test: /\.s[ac]ss$/i,
                use: [
                    'style-loader',
                    {
                        loader: 'css-loader',
                        options: { url: false },
                    },
                    'postcss-loader',
                    'resolve-url-loader',

                    {
                        loader: 'sass-loader',
                        options: {
                            sourceMap: true,
                            sassOptions: {
                                sourceMapContents: true,
                            },
                        },
                    },
                ],
            },
            {
                test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/,
                type: 'asset',
            },
        ],
    },
};

Finally [.bablerc]:

{
    "presets": ["@babel/preset-env", "@babel/preset-react"]
}
0 likes
13 replies
bzarboni's avatar

Oh, and I have a stub in my test for route:

describe('<SupportForm>', () => {
    beforeEach(() => {
        const submitUrl = 'support.store';
        const cancelUrl = 'users.index';

		cy.stub(route).as('route');
        cy.mount(<SupportForm submitUrl={submitUrl} cancelUrl={cancelUrl} />);
    });
...
}
bzarboni's avatar

I tried installing npm install --save-dev ziggy-js (with no luck) is that the right dependency?

(uncaught exception)TypeError: (0 , _ziggyJs.route) is not a function

Sinnbeck's avatar

@bzarboni are you sure you set you code to import it?

import route from 'ziggy-js' 
1 like
bzarboni's avatar

@Sinnbeck Thanks for the suggestion! I did. I added it to both the spec/test file, and the component. When I added it to the component, I get the error above

uncaught exception)TypeError: (0 , _ziggyJs.route) is not a function

bzarboni's avatar

@Sinnbeck For sure!

[SupportForm.cy.js]:

import React from 'react';
import SupportForm from '@/Components/Support/SupportForm';
import { Inertia } from '@inertiajs/inertia';
import { route } from 'ziggy-js';

const subjectInput = '#subject';
const descriptionInput = '#description';

describe('<SupportForm>', () => {
    beforeEach(() => {
        const submitUrl = 'support.store';
        const cancelUrl = 'users.index';

        cy.stub(route).as('route');
        cy.mount(<SupportForm submitUrl={submitUrl} cancelUrl={cancelUrl} />);
    });

    it('Can be filled in', () => {
        cy.get(subjectInput).type('Testing the support component');
    });

    it('Can be cancelled', () => {
        // Fill in the subject
        const cancelSubject = 'Test pressing the Cancel button';
        cy.get(subjectInput).type(cancelSubject);
        cy.get(subjectInput).should('have.value', cancelSubject);

        // Fill in the description
        const description = 'This is a very nice description of something.';
        cy.get(descriptionInput).type(description);
        cy.get(descriptionInput).should('have.value', description);

        // Setup a stub for the button action
        cy.stub(Inertia, 'visit').as('cancelButtonClicked');
        // Press Cancel
        cy.contains('button', 'Cancel').click();
        // expect(cancelButtonClicked).to.be.calledOnce;
    });
});

[Component SupportForm.js]:

import React, { useState } from 'react';
import { Col, Row, Container, Alert, Form, Button } from 'react-bootstrap';
import { useForm } from '@inertiajs/inertia-react';
import BaseInput from '@/Components/Base/BaseInput';
import BaseButton from '@/Components/Base/BaseButton';
import { Inertia } from '@inertiajs/inertia';
import PropTypes from 'prop-types';
import { route } from 'ziggy-js';

export default function SupportForm({ submitUrl, cancelUrl }) {
    const { data, setData, post, processing, errors, reset } = useForm({
        subject: '',
        description: '',
    });

    const [showSuccess, setShowSuccess] = useState(false);

    const submit = (e) => {
        e.preventDefault();
        post(route(submitUrl, route().params.team), {
            onSuccess: () => {
                setShowSuccess(true);
                reset();
            },
            onError: (error) => console.log(error),
        });
    };

    return (
        <Form onSubmit={submit}>
            <Container className='support-page'>
                <Row>
                    <Col md={{ span: 6, offset: 3 }}>
                        <Row className='mt-5 pt-2'>
                            <Col md={12} className='my-5 text-center'>
                                <h2 className='bold'>
                                    Send a request to our support team and we’ll
                                    get back to you within one business day.
                                </h2>
                            </Col>
                            {showSuccess && (
                                <Col md={12}>
                                    <Alert
                                        dismissible
                                        variant='success'
                                        onClose={() => setShowSuccess(false)}
                                    >
                                        Request send successfully
                                    </Alert>
                                </Col>
                            )}
                            <Col md={12}>
                                <BaseInput
                                    label='Subject'
                                    name='subject'
                                    value={data.subject}
                                    isFocused={true}
                                    handleChange={(e) =>
                                        setData('subject', e.target.value)
                                    }
                                    required
                                    errors={errors}
                                />
                            </Col>
                            <Col md={12}>
                                <BaseInput
                                    label='Description'
                                    name='description'
                                    value={data.description}
                                    as='textarea'
                                    handleChange={(e) =>
                                        setData('description', e.target.value)
                                    }
                                    required
                                    errors={errors}
                                />
                            </Col>

                            <Col md={12} className=''>
                                <Row>
                                    <Col md={{ span: 4, offset: 2 }}>
                                        <Button
                                            variant='outline-secondary'
                                            className='w-100 py-2 my-1 d-flex align-items-center justify-content-center'
                                            onClick={() =>
                                                Inertia.visit(
                                                    route(cancelUrl, [
                                                        route().params.team,
                                                    ])
                                                )
                                            }
                                        >
                                            Cancel
                                        </Button>
                                    </Col>
                                    <Col md={{ span: 4 }}>
                                        <BaseButton
                                            variant='success'
                                            classList='w-100 py-2 my-1 d-flex align-items-center justify-content-center'
                                            processing={processing}
                                        >
                                            Send Request
                                        </BaseButton>
                                    </Col>
                                </Row>
                            </Col>
                        </Row>
                    </Col>
                </Row>
            </Container>
        </Form>
    );
}

SupportForm.propTypes = {
    submitUrl: PropTypes.string,
    cancelUrl: PropTypes.string,
};

SupportForm.defaultProps = {
    submitUrl: 'support.store',
    cancelUrl: 'users.index',
};

Sinnbeck's avatar

@bzarboni I'm unsure if it's a named export or just default. Have you tried importing as default?

import route from 'ziggy-js' 
Sinnbeck's avatar

Can you get it to work in your regular app with the npm version?

bzarboni's avatar

@Sinnbeck thanks for the suggestion. I've just tried importing as default, and I get errors in my application code:

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'call')
    at __webpack_require__ (manifest.js:31:42)
    at eval (SupportForm.js:18:66)
    at ./resources/js/Components/Support/SupportForm.js (resources_js_Pages_Tenancy_Support_Index_js.js:68:1)
    at __webpack_require__ (manifest.js:31:42)
    at eval (Index.js:8:89)
    at ./resources/js/Pages/Tenancy/Support/Index.js (resources_js_Pages_Tenancy_Support_Index_js.js:98:1)
    at __webpack_require__ (manifest.js:31:42)
    at app.js:588:10

And the component won't load.

If I try with import { route } from 'ziggy-js'; though, the component loads, but throws errors on POST:

SupportForm.js?f4a8:20 Uncaught TypeError: (0 , ziggy_js__WEBPACK_IMPORTED_MODULE_5__.route) is not a function
    at submit (SupportForm.js?f4a8:20:36)
    at HTMLUnknownElement.callCallback (react-dom.development.js?ac89:3945:1)
    at Object.invokeGuardedCallbackDev (react-dom.development.js?ac89:3994:1)
    at invokeGuardedCallback (react-dom.development.js?ac89:4056:1)
    at invokeGuardedCallbackAndCatchFirstError (react-dom.development.js?ac89:4070:1)
    at executeDispatch (react-dom.development.js?ac89:8243:1)
    at processDispatchQueueItemsInOrder (react-dom.development.js?ac89:8275:1)
    at processDispatchQueue (react-dom.development.js?ac89:8288:1)
    at dispatchEventsForPlugins (react-dom.development.js?ac89:8299:1)
    at eval (react-dom.development.js?ac89:8508:1)
Sinnbeck's avatar

@bzarboni that's really strange. I use it myself in my own react app without any problems. Only difference is that I use vite instead of mix. But I'm pretty sure it worked before I migrated to vite. I cannot currently think of a reason why it claims to not be a function.. Did you remove the ziggy alias from your webpack?

bzarboni's avatar

@Sinnbeck Hah, yeah, it has me stumped for sure! Thanks for your suggestions so far - I did remove the alias from the webpack config

Sinnbeck's avatar

@bzarboni I'll try and remember to check my own code when I'm at my computer

Sadly I haven't used cypress 10 for testing yet (it's on my todo)

1 like

Please or to participate in this conversation.