Cypress component tests for Nuxt with Tailwind - javascript

I'm trying to set up Cypress component tests for a Nuxt app. It works, but almost none of my styles are working, because they depend on Tailwind together with my custom tailwind.config.js.
This is my cypress/plugins/index.js:
const { startDevServer } = require('#cypress/webpack-dev-server');
const { getWebpackConfig } = require('nuxt');
module.exports = (on, config) => {
on('dev-server:start', async (options) => {
const webpackConfig = await getWebpackConfig();
return startDevServer({
options,
webpackConfig,
});
});
return config;
};
How can I include Tailwind with a custom config? And while we're at it: how do I include an extra global .scss files in all my component tests?

You can get your styles to work by importing them directly into your test files, like so:
// YourComponent.spec.js
import 'tailwindcss/dist/tailwind.min.css'
import '#/assets/css/tailwind.css'
I'm looking into a method to add these styles globally as well, so if I find something I'll make sure to post it here.
Updated answer
Add the following to your build settings in your nuxt.config.js file.
// nuxt.config.js
import { join } from 'path'
...
plugins: {
tailwindcss: join(__dirname, 'tailwind.config.js'),
...
},
If you have jit mode enabled for tailwindcss, make sure to add the appropriate purge paths to your tailwind config. With me it was loading in an infinite loop, after specifying these paths more specific it was sorted out. Also see tailwind docs.
// tailwind.config.js
purge: [
'./components/**/*.vue',
'./layouts/**/*.vue',
'./pages/**/*.vue'
],

Related

How to set a base path for SCSS imports in SvelteKit?

I want to import an scss file in one of my components. Is there a way to use aliases or something else so I can import them without a long relative path.
For example, rather than this:
<style lang='scss'> #use '../../../styles/main.scss' </style>
I would like to do something like this instead:
<style lang='scss'> #use '#/styles/main.scss' </style>
I'm using the sass a svelte-preprocess packages.
This works in Vue but not sure if a similar thing is possible in svelte.
OK so after quite a lot of googling and experimenting, I think I have come up with a solution.
Firstly I switched to using the vitePreprocess processor rather than svelte-preprocess. As per the instructions, svelte.config.js needed to be edited to look like this:
import adapter from '#sveltejs/adapter-auto';
import { vitePreprocess } from '#sveltejs/kit/vite';
/** #type {import('#sveltejs/kit').Config} */
const config = {
kit: {
adapter: adapter()
},
preprocess: [vitePreprocess()] // Add this line and its import (above)
};
export default config;
Then in vite.config.js edit it as follows:
import { sveltekit } from '#sveltejs/kit/vite'
import path from 'path'
/** #type {import('vite').UserConfig} */
const config = {
plugins: [
sveltekit()
],
resolve: {
alias: {
'#': path.resolve('src') // Styles in src/styles will be accessible as '#/styles/whatever.scss'
}
}
}
export default config
This achieves my goal though hot module reload doesn't quite work correctly. Changes to the scss file don't trigger an automatic reload. There are probably a few options but I like the following which uses the vite-plugin-restart module to watch the styles folder and restart the server when one of those files changes.
First:
npm install --save-dev vite-plugin-restart
Then further edit vite.config.js to look something like this:
import { sveltekit } from '#sveltejs/kit/vite'
import VitePluginRestart from 'vite-plugin-restart'
import path from 'path'
// No idea why this is needed
// The default export of vite-plugin-restart looks to be the function but it doesn't work when imported
// Need to access the 'default' key from the imported object instead
const ViteRestart = VitePluginRestart.default
/** #type {import('vite').UserConfig} */
const config = {
plugins: [
sveltekit(),
ViteRestart({
restart: [
'src/styles/*.scss' // For some reason path.resolve doesn't seem to work here
]
})
],
resolve: {
alias: {
'#': path.resolve('src')
}
}
}
export default config
The vite server will be restarted whenever you edit one of the scss files.

Overwrite an existing plugin JS in Shopware 6

I am currently trying to overwrite a javascript file from an existing plugin.
I've been following the documentation but I am struggling with the path for the JS class to overwrite.
In the docs is an example code:
import CookiePermissionPlugin from 'src/plugin/cookie/cookie-permission.plugin';
export default class MyCookiePermission extends CookiePermissionPlugin {
}
So I implemented the following code:
import QuantityField from 'src/plugin/FilterRangeSlider/filter-range-slider.plugin';
export default class ExampleQuantityField extends QuantityField {
This code does not work for me, since the original file is in the vendor directory and my plugin is in the custom directory. When trying to compile (eg bin/build-storefront.sh) I receive the following error message:
Module not found: Error: Can't resolve 'src/plugin/FilterRangeSlider/filter-range-slider.plugin' in '<project root>/custom/plugins/ExampleProductFilter/src/Resources/app/storefront/src/filter-range-slider'
Any idea how I can import that class as stated in the docs?
Node.js provides a bunch of in-built file-system functionalities. The __dirname points to the root directory.
So, this should work.
import QuantityField from `${__dirname}/vendor/store.shopware.com/mmeesrangesliderpro/src/Resources/app/storefront/src/script/filter-range-slider.plugin`
My current solution is not really clean...
import QuantityField from '../../../../../../../../../vendor/store.shopware.com/mmeesrangesliderpro/src/Resources/app/storefront/src/script/filter-range-slider.plugin';
Isnt there any plugin root variable or something similar?
There is - I believe - no easier way to accomplish this.
If each plugin would extend the webpack config as described in
https://developer.shopware.com/docs/guides/plugins/plugins/administration/extending-webpack
const path = require('path');
module.exports = () => {
return {
resolve: {
alias: {
MmeesRangeSliderPro: path.join(__dirname, '..', 'src')
}
}
};
};
The alias could be used instead of the Plugin Root.
But this is not the case, so the following is not working:
import QuantityField from 'MmeesRangeSliderPro/plugin/FilterRangeSlider/filter-range-slider.plugin';
You can add a console.log(webpackConfig) to the bottom of Resources/app/storefront/webpack.config.js to validate this.
alias: {
src: '/home/user/example/projects/example.de/vendor/shopware/storefront/Resources/app/storefront/src',
assets: '/home/user/example/projects/example.de/vendor/shopware/storefront/Resources/app/storefront/assets',
jquery: 'jquery/dist/jquery.slim',
scss: '/home/user/example/projects/example.de/vendor/shopware/storefront/Resources/app/storefront/src/scss',
vendor: '/home/user/example/projects/example.de/vendor/shopware/storefront/Resources/app/storefront/vendor'
}
And those again to not really allow locating the module.
If it is a third-party plugin, replace the path with an absolute path like the following
import ThirdPartyPlugin from '/app/custom/plugins/ThirdPartyPlugin/src/Resources/app/storefront/src/third-party-plugin/third-party-plugin.plugin';

Gatsby Plug-in: How Can I Take a Component as a Plug-In Option?

I'm trying to make an improvement to an existing Gatsby plug-in, and I want to pass a React Component to the plug-in, through its configuration entry in gatsby-config.js:
plugins: [
{
resolve: `gatsby-plugin-modal-routing`,
options: { someComponent: SomeComponentClassOrFunction }
},
However, the problem I'm running into is that I can't figure out how to make it work.
If I try to pass the component itself as part of the plug-in's configuration, it seems to get serialized to/from JSON, resulting in the class becoming a useless object. So it seems I have to pass a path string instead.
plugins: [
{
resolve: `gatsby-plugin-modal-routing`,
options: {
modalComponentPath: path.join(__dirname, 'src/components/SomeComponent.js')
}
},
However, if I try to pass the path instead, I can't figure out how to use it to load the component inside the plug-in. I've tried using a dynamic Node import (ie. import(path).then(component => ...)) ...
with a path that's path.join-ed with __dirname
with a relative path (src/components/SomeComponent)
with a local-path-relative path (./src/components/SomeComponent)
with and without a trailing .js
I'm not sure if this is some sort of issue with the different paths of the app vs. the plug-in or whether there's some other problem, but using import seems like an un-Gatsby-like solution anyway.
So, then I discovered the loadPage and loadPageSync functions which are passed into the plug-in ... but those failed also. Every path I try results in component coming back ... but it's a "page not found" component (presumably because the component I'm trying to pass in hasn't been added as a page).
This seems like it should be a simple question, at least to anyone who has worked on Gatsby plug-ins before: if you want a plug-in to take a component as an input (either as a function/class or as a string of a path to a module) ... how can you actually use that component in your plug-in?
All I'm looking for is a basic pattern or reference to a line in an existing Gatsby plugin that takes a component, or something simple like that (I can look up any details).
This seems like it should be a simple question
I had the same thought while trying this out myself. Oh boy.
TL:DR
// gatsby-node.js
const { DefinePlugin } = require('webpack')
const path = require('path')
exports.onCreateWebpackConfig = ({ actions }, { componentPath }) => {
actions.setWebpackConfig({
plugins: [
new DefinePlugin({
'___COMPONENT___': JSON.stringify(componentPath)
})
]
})
}
// gatsby-ssr
export const onRenderBody = ({ setPreBodyComponents }) => {
const Component = require(___COMPONENT___).default
setPreBodyComponents([<Component />])
}
Long read
Gatsby config doesn't seem to pass functions around (I could have sworn it used to), so passing a React component directly to your custom plugin is out the window. It has to be a path to your component.
// gatsby-config.js
{
resolve: 'my-custom-plugin',
options: {
componentPath: path.join(__dirname, './my-component.js')
}
}
You didn't say if you're using the component in gatsby-node or gatsby-browser/ssr, but I assume it's the later since requiring stuff dynamically in Node is dead simple:
Gatsby Node
// gatsby-node.js
function consume(component) {
const Component = require(component)
}
...although it doesn't understand JSX or ESM, but that's a different problem.
Gatsby Browser
gatsby-browser/ssr is run with webpack, so the module format is not a problem. But import(componentPath) won't work:
Dynamic expressions in import()
It is not possible to use a fully dynamic import statement, such as import(foo). Because foo could potentially be any path to any file in your system or project.
webpack doc
Ok, I suppose so something like this should work:
// gatsby-browser
import('./my-dir' + componentPath)
Nope, because webpack will try to resolve this from wherever the plugin live, i.e node_modules or plugins directory & we're not about to ask our users to put their custom components in node_modules.
What about this, then?
// gatsby-browser
import(process.cwd() + componentPath) // nope
We're right back at the beginning — webpack doesn't like full dynamic path! And also even if this works, this is a terrible idea since webpack will try to bundle the whole working directory.
Only if we could encode the path as a static string beforehand, so webpack can just read that code — like using webpack.DefinePlugin to define environment variables. Fortunately we can do that in gatsby-node.js:
// gatsby-node.js
const { DefinePlugin } = require('webpack')
const path = require('path')
exports.onCreateWebpackConfig = ({ actions }) => {
actions.setWebpackConfig({
plugins: [
new DefinePlugin({
'___CURRENT_DIR___': JSON.stringify(process.cwd())
})
]
})
}
And finally
// gatsby-browser
// eslint throw error for unknown var, so disable it
// eslint-disable-next-line
import(___CURRENT_DIR___ + componentPath) // works, but don't do this
But since we can access user options right in gatsby-node, let's just encode the whole path:
// gatsby-node.js
const { DefinePlugin } = require('webpack')
- const path = require('path')
- exports.onCreateWebpackConfig = ({ actions }) => {
+ exports.onCreateWebpackConfig = ({ actions }, { componentPath }) => {
actions.setWebpackConfig({
plugins: [
new DefinePlugin({
- '___CURRENT_DIR___': JSON.stringify(process.cwd())
+ '___COMPONENT___': JSON.stringify(componentPath)
})
]
})
}
Back in gatsby-browser.js:
// gatsby-browser
// I pick a random API to test, can't imagine why one would import a module in this API
export const onRouteUpdate = async () => {
// eslint-disable-next-line
const { default: Component } = await import(___COMPONENT___)
console.log(Component) // works
}
Gatsby SSR
For the sake of completeness, let's try the same trick in gatby-ssr:
// gatsby-ssr
export const onRenderBody = async ({ setPreBodyComponents }) => {
// const Component = require(___COMPONENT___).default
const { default: Component } = await import(___COMPONENT___)
setPreBodyComponents([<Component />])
}
...and it failed.
Why? If one's curious enough they might go and dig around Gatsby code to see how gatsby-ssr is treated differently than gatsby-browser, but alas I just don't feel like doing that.
Fear not, we still have one trick up our sleeve. Webpack's require can import module dynamically too, though not asynchronously. Since gatsby-ssr doesn't run in the browser, I couldn't care less about asynchronicity.
export const onRenderBody = ({ setPreBodyComponents }) => {
const Component = require(___COMPONENT___).default
setPreBodyComponents([<Component />]) // works
}
And now it works.
Sharing code between gatsby-ssr & gatsby-browser
Let's just say we need this component in both gatsby-ssr and gatsby-browser — would require(...) works in gatsby-browser too?
export const onRouteUpdate = async () => {
// eslint-disable-next-line
const { default: Component } = require(___COMPONENT___)
console.log(Component) // yes
}
It works.
import(..) vs require()
While import() does load stuff dynamically, it is more of a tool for code-splitting. Here's some different, other than asynchronicity:
using import('./my-dir' + componentPath) will bundle all files inside ./my-dir into a chunk. There's magic comment we can use to exclude/include stuff.
require(...) will just inline the required component into whatever chunk's calling it.

Jest global variables dynamically imported from a JS file (export.module)

I need to import a .js file with config values to be used on my react app:
import config from './config'
These values are already added at webpack configuration:
new webpack.DefinePlugin({...config})
What I need is to import these values into jest.config.js:
globals: {
config: // Here config...
}
"I know that we can add these values manually, but I want to add them from this file to prevent the maintenance of all values instead".
Thanks!
https://jestjs.io/docs/en/configuration#globals-object
Note that, if you specify a global reference value (like an object or array) here, and some code mutates that value in the midst of running a test, that mutation will not be persisted across test runs for other test files. In addition the globals object must be json-serializable, so it can't be used to specify global functions.
here is a guide on adding globals to your jest config with basic usage below
import * as config from "path/to/config";
//...package.json || jest.config
"jest": {
"globals": {
...config
}
}
const config = {
this: 'is now global'
}
console.log({
jest: {
globals: {
...config
}
}
})
You can add it to the globals object and import the config as normal

How can I use a config file in React?

Let's say I have 5 jsx files and each file uses some config parameter.
My index.js file imports all of these 5 jsx files.
Instead of having my config data spread accross 5 files, is there a way for my jsx files to get the data from a global JS object which has loaded the data from a config file?
I've seen some examples, but I've not been able to get them to work.
JS6 import function | Example using webpack
Assuming ES6:
config.js
export const myConfig = { importantData: '', apiUrl: '', ... };
Then:
jsxFileOne.js, jsxFileTwo.js, ...
import { myConfig } from 'config.js';
There are other ways to import & export things globally leveraging webpack, but this should get you started.
If your project is built using Webpack, consider using node-env-file.
Example config file snippets:
development.env
API_SERVER_URL=https://www.your-server.com
webpack.config.js
const envFile = require('node-env-file');
...
const appSettingsFile = isDevBuild ? '/settings/development.env' : '/settings/production.env';
try {
envFile(path.join(__dirname + appSettingsFile));
} catch (error) {
console.log("Failed to read env file!: " + __dirname + appSettingsFile);
}
...
plugins: [
new webpack.DefinePlugin({
"process.env": {
API_SERVER_URL: JSON.stringify(process.env.API_SERVER_URL)
}
})
...
]
Inside your js/jsx code, you can now access process.env.API_SERVER_URL variable which will contain the required value.
It seems dotenv package is more popular, you can try this out as well.
Very old problem, that nobody took the time to solve, until now. I leave this for future readers because this is a top search result for configuration in React.
I created wj-config to deal exactly with this. Be sure to pay close attention to the React notes as you will need to enable top-level awaits in webpack, either by ejecting or using the #craco/craco NPM package.
You may also read this blog post that explains its use.

Categories

Resources