Skip to content

Commit

Permalink
minor #1343 Add tests for CSS Modules with React and Preact (Kocal)
Browse files Browse the repository at this point in the history
This PR was squashed before being merged into the main branch.

Discussion
----------

Add tests for CSS Modules with React and Preact

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | no <!-- please update CHANGELOG.md file -->
| Deprecations? | no <!-- please update CHANGELOG.md file -->
| Issues        | Fix #... <!-- prefix each issue number with "Fix #", no need to create an issue if none exists, explain below instead -->
| License       | MIT

<!--
Replace this notice by a description of your feature/bugfix.
This will help reviewers and should be a good start for the documentation.

Additionally (see https://symfony.com/releases):
 - Always add tests and ensure they pass.
 - Features and deprecations must be submitted against the latest branch.
 - For new features, provide some code snippets to help understand usage.
 - Changelog entry should follow https://symfony.com/doc/current/contributing/code/conventions.html#writing-a-changelog-entry
 - Never break backward compatibility.
-->

Quick PR to add functional tests about using CSS Modules with React and Preact, to see it it also break CSS Modules with them in #1319.

Commits
-------

c22943b Add tests for CSS Modules with React
dbd5e5d Add tests for CSS Modules with Preact
  • Loading branch information
Kocal committed Sep 20, 2024
2 parents b212b15 + c22943b commit 675d68d
Show file tree
Hide file tree
Showing 25 changed files with 296 additions and 11 deletions.
13 changes: 13 additions & 0 deletions fixtures/preact-css-modules/components/App.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { h } from 'preact';
import './styles.css';
import './styles.less';
import './styles.scss';
import './styles.stylus';
import stylesCss from './styles.module.css?module';
import stylesLess from './styles.module.less?module';
import stylesScss from './styles.module.scss?module';
import stylesStylus from './styles.module.stylus?module';

export default function App() {
return <div className={`red large justified lowercase ${stylesCss.italic} ${stylesLess.underline} ${stylesScss.bold} ${stylesStylus.rtl}`}></div>
}
3 changes: 3 additions & 0 deletions fixtures/preact-css-modules/components/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.red {
color: red;
}
3 changes: 3 additions & 0 deletions fixtures/preact-css-modules/components/styles.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.justified {
text-align: justify;
}
3 changes: 3 additions & 0 deletions fixtures/preact-css-modules/components/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.italic {
font-style: italic;
}
3 changes: 3 additions & 0 deletions fixtures/preact-css-modules/components/styles.module.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.underline {
text-decoration: underline;
}
3 changes: 3 additions & 0 deletions fixtures/preact-css-modules/components/styles.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.bold {
font-weight: bold;
}
2 changes: 2 additions & 0 deletions fixtures/preact-css-modules/components/styles.module.stylus
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.rtl
direction: rtl;
3 changes: 3 additions & 0 deletions fixtures/preact-css-modules/components/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.large {
font-size: 50px;
}
2 changes: 2 additions & 0 deletions fixtures/preact-css-modules/components/styles.stylus
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.lowercase
text-transform: lowercase
5 changes: 5 additions & 0 deletions fixtures/preact-css-modules/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { h, render } from 'preact';

import App from './components/App';

render(<App />, document.getElementById('app'));
12 changes: 12 additions & 0 deletions fixtures/react-css-modules/components/App.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import './styles.css';
import './styles.less';
import './styles.scss';
import './styles.stylus';
import stylesCss from './styles.module.css?module';
import stylesLess from './styles.module.less?module';
import stylesScss from './styles.module.scss?module';
import stylesStylus from './styles.module.stylus?module';

export default function App() {
return <div className={`red large justified lowercase ${stylesCss.italic} ${stylesLess.underline} ${stylesScss.bold} ${stylesStylus.rtl}`}></div>
}
3 changes: 3 additions & 0 deletions fixtures/react-css-modules/components/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.red {
color: red;
}
3 changes: 3 additions & 0 deletions fixtures/react-css-modules/components/styles.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.justified {
text-align: justify;
}
3 changes: 3 additions & 0 deletions fixtures/react-css-modules/components/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.italic {
font-style: italic;
}
3 changes: 3 additions & 0 deletions fixtures/react-css-modules/components/styles.module.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.underline {
text-decoration: underline;
}
3 changes: 3 additions & 0 deletions fixtures/react-css-modules/components/styles.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.bold {
font-weight: bold;
}
2 changes: 2 additions & 0 deletions fixtures/react-css-modules/components/styles.module.stylus
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.rtl
direction: rtl;
3 changes: 3 additions & 0 deletions fixtures/react-css-modules/components/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.large {
font-size: 50px;
}
2 changes: 2 additions & 0 deletions fixtures/react-css-modules/components/styles.stylus
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.lowercase
text-transform: lowercase
6 changes: 6 additions & 0 deletions fixtures/react-css-modules/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import {createRoot} from 'react-dom/client';
import App from './components/App';

const root = createRoot(document.getElementById('app'));

root.render(<App />);
5 changes: 4 additions & 1 deletion lib/loaders/babel.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@ module.exports = {
if (webpackConfig.useReact) {
loaderFeatures.ensurePackagesExistAndAreCorrectVersion('react');

babelConfig.presets.push(require.resolve('@babel/preset-react'));
babelConfig.presets.push([require.resolve('@babel/preset-react'), {
// TODO: To remove when Babel 8, "automatic" will become the default value
runtime: 'automatic',
}]);
}

if (webpackConfig.usePreact) {
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"@babel/eslint-parser": "^7.17.0",
"@babel/plugin-transform-react-jsx": "^7.12.11",
"@babel/preset-env": "^7.16.0",
"@babel/preset-react": "^7.0.0",
"@babel/preset-react": "^7.9.0",
"@babel/preset-typescript": "^7.0.0",
"@hotwired/stimulus": "^3.0.0",
"@symfony/mock-module": "file:fixtures/stimulus/mock-module",
Expand Down Expand Up @@ -81,6 +81,8 @@
"preact": "^10.5.0",
"preact-compat": "^3.17.0",
"puppeteer": "^23.2.2",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"sass": "^1.17.0",
"sass-loader": "^16.0.1",
"sinon": "^14.0.0",
Expand All @@ -102,7 +104,7 @@
"@babel/core": "^7.17.0",
"@babel/plugin-transform-react-jsx": "^7.12.11",
"@babel/preset-env": "^7.16.0",
"@babel/preset-react": "^7.0.0",
"@babel/preset-react": "^7.9.0",
"@babel/preset-typescript": "^7.0.0",
"@symfony/stimulus-bridge": "^3.0.0",
"@vue/babel-helper-vue-jsx-merge-props": "^1.0.0",
Expand Down
172 changes: 168 additions & 4 deletions test/functional.js
Original file line number Diff line number Diff line change
Expand Up @@ -1684,13 +1684,179 @@ module.exports = {
expectClassDeclaration('large'); // Standard SCSS
expectClassDeclaration('justified'); // Standard Less
expectClassDeclaration('lowercase'); // Standard Stylus
expectClassDeclaration('block'); // Standard Postcss
expectClassDeclaration('block'); // Standard PostCSS

expectClassDeclaration('italic_foo'); // CSS Module
expectClassDeclaration('bold_foo'); // SCSS Module
expectClassDeclaration('underline_foo'); // Less Module
expectClassDeclaration('rtl_foo'); // Stylus Module
expectClassDeclaration('hidden_foo'); // PostCSS Module

testSetup.requestTestPage(
browser,
path.join(config.getContext(), 'www'),
[
'build/runtime.js',
'build/main.js'
],
async({ page }) => {
const divClassArray = await page.evaluate(() => Array.from(document.body.querySelector('#app > div').classList.values()));

expect(divClassArray.includes('red')).to.be.true; // Standard CSS
expect(divClassArray.includes('large')).to.be.true; // Standard SCSS
expect(divClassArray.includes('justified')).to.be.true; // Standard Less
expect(divClassArray.includes('lowercase')).to.be.true; // Standard Stylus
expect(divClassArray.includes('block')).to.be.true; // Standard PostCSS

expect(divClassArray.includes('italic_foo')).to.be.true; // CSS module
expect(divClassArray.includes('bold_foo')).to.be.true; // SCSS module
expect(divClassArray.includes('underline_foo')).to.be.true; // Less module
expect(divClassArray.includes('rtl_foo')).to.be.true; // Stylus module
expect(divClassArray.includes('hidden_foo')).to.be.true; // PostCSS module

done();
}
);
});
});

it('React supports CSS/Sass/Less/Stylus modules', (done) => {
const appDir = testSetup.createTestAppDir();
const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev');
config.enableSingleRuntimeChunk();
config.setPublicPath('/build');
config.addEntry('main', './react-css-modules/main.js');
config.enableReactPreset();
config.enableSassLoader();
config.enableLessLoader();
config.enableStylusLoader();
config.configureCssLoader(options => {
// Remove hashes from local ident names
// since they are not always the same.
if (options.modules) {
options.modules.localIdentName = '[local]_foo';
}
});

// Enable the PostCSS loader so we can use `lang="postcss"`
config.enablePostCssLoader();
fs.writeFileSync(
path.join(appDir, 'postcss.config.js'),
`
module.exports = {
plugins: [
require('autoprefixer')()
]
} `
);

testSetup.runWebpack(config, (webpackAssert) => {
expect(config.outputPath).to.be.a.directory().with.deep.files([
'main.js',
'main.css',
'manifest.json',
'entrypoints.json',
'runtime.js',
]);

const expectClassDeclaration = (className) => {
webpackAssert.assertOutputFileContains(
'main.css',
`.${className} {`
);
};

expectClassDeclaration('red'); // Standard CSS
expectClassDeclaration('large'); // Standard SCSS
expectClassDeclaration('justified'); // Standard Less
expectClassDeclaration('lowercase'); // Standard Stylus

expectClassDeclaration('italic_foo'); // CSS Module
expectClassDeclaration('bold_foo'); // SCSS Module
expectClassDeclaration('underline_foo'); // Less Module
expectClassDeclaration('rtl_foo'); // Stylus Module

testSetup.requestTestPage(
browser,
path.join(config.getContext(), 'www'),
[
'build/runtime.js',
'build/main.js'
],
async({ page }) => {
const divClassArray = await page.evaluate(() => Array.from(document.body.querySelector('#app > div').classList.values()));

expect(divClassArray.includes('red')).to.be.true; // Standard CSS
expect(divClassArray.includes('large')).to.be.true; // Standard SCSS
expect(divClassArray.includes('justified')).to.be.true; // Standard Less
expect(divClassArray.includes('lowercase')).to.be.true; // Standard Stylus

expect(divClassArray.includes('italic_foo')).to.be.true; // CSS module
expect(divClassArray.includes('bold_foo')).to.be.true; // SCSS module
expect(divClassArray.includes('underline_foo')).to.be.true; // Less module
expect(divClassArray.includes('rtl_foo')).to.be.true; // Stylus module

done();
}
);
});
});

it('Preact supports CSS/Sass/Less/Stylus modules', (done) => {
const appDir = testSetup.createTestAppDir();
const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev');
config.enableSingleRuntimeChunk();
config.setPublicPath('/build');
config.addEntry('main', './preact-css-modules/main.js');
config.enablePreactPreset();
config.enableSassLoader();
config.enableLessLoader();
config.enableStylusLoader();
config.configureCssLoader(options => {
// Remove hashes from local ident names
// since they are not always the same.
if (options.modules) {
options.modules.localIdentName = '[local]_foo';
}
});

// Enable the PostCSS loader so we can use `lang="postcss"`
config.enablePostCssLoader();
fs.writeFileSync(
path.join(appDir, 'postcss.config.js'),
`
module.exports = {
plugins: [
require('autoprefixer')()
]
} `
);

testSetup.runWebpack(config, (webpackAssert) => {
expect(config.outputPath).to.be.a.directory().with.deep.files([
'main.js',
'main.css',
'manifest.json',
'entrypoints.json',
'runtime.js',
]);

const expectClassDeclaration = (className) => {
webpackAssert.assertOutputFileContains(
'main.css',
`.${className} {`
);
};

expectClassDeclaration('red'); // Standard CSS
expectClassDeclaration('large'); // Standard SCSS
expectClassDeclaration('justified'); // Standard Less
expectClassDeclaration('lowercase'); // Standard Stylus

expectClassDeclaration('italic_foo'); // CSS Module
expectClassDeclaration('bold_foo'); // SCSS Module
expectClassDeclaration('underline_foo'); // Less Module
expectClassDeclaration('rtl_foo'); // Stylus Module
expectClassDeclaration('hidden_foo'); // Stylus Module

testSetup.requestTestPage(
browser,
Expand All @@ -1706,13 +1872,11 @@ module.exports = {
expect(divClassArray.includes('large')).to.be.true; // Standard SCSS
expect(divClassArray.includes('justified')).to.be.true; // Standard Less
expect(divClassArray.includes('lowercase')).to.be.true; // Standard Stylus
expect(divClassArray.includes('block')).to.be.true; // Standard Stylus

expect(divClassArray.includes('italic_foo')).to.be.true; // CSS module
expect(divClassArray.includes('bold_foo')).to.be.true; // SCSS module
expect(divClassArray.includes('underline_foo')).to.be.true; // Less module
expect(divClassArray.includes('rtl_foo')).to.be.true; // Stylus module
expect(divClassArray.includes('hidden_foo')).to.be.true; // Stylus module

done();
}
Expand Down
18 changes: 16 additions & 2 deletions test/loaders/babel.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,23 @@ describe('loaders/babel', () => {

// env, react & foo
expect(actualLoaders[0].options.presets).to.have.lengthOf(3);
expect(actualLoaders[0].options.presets).to.include(require.resolve('@babel/preset-react'));
expect(actualLoaders[0].options.presets[0]).to.deep.equal([
require.resolve('@babel/preset-env'),
{
corejs: null,
modules: false,
targets: {},
useBuiltIns: false,
},
]);
expect(actualLoaders[0].options.presets[1]).to.deep.equal([
require.resolve('@babel/preset-react'),
{
runtime: 'automatic',
}
]);
// foo is also still there, not overridden
expect(actualLoaders[0].options.presets).to.include('foo');
expect(actualLoaders[0].options.presets[2]).to.equal('foo');
});

it('getLoaders() with preact', () => {
Expand Down
Loading

0 comments on commit 675d68d

Please # to comment.