Skip to content

Commit 675d68d

Browse files
committed
minor #1343 Add tests for CSS Modules with React and Preact (Kocal)
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
2 parents b212b15 + c22943b commit 675d68d

25 files changed

+296
-11
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { h } from 'preact';
2+
import './styles.css';
3+
import './styles.less';
4+
import './styles.scss';
5+
import './styles.stylus';
6+
import stylesCss from './styles.module.css?module';
7+
import stylesLess from './styles.module.less?module';
8+
import stylesScss from './styles.module.scss?module';
9+
import stylesStylus from './styles.module.stylus?module';
10+
11+
export default function App() {
12+
return <div className={`red large justified lowercase ${stylesCss.italic} ${stylesLess.underline} ${stylesScss.bold} ${stylesStylus.rtl}`}></div>
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.red {
2+
color: red;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.justified {
2+
text-align: justify;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.italic {
2+
font-style: italic;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.underline {
2+
text-decoration: underline;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.bold {
2+
font-weight: bold;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.rtl
2+
direction: rtl;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.large {
2+
font-size: 50px;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.lowercase
2+
text-transform: lowercase

fixtures/preact-css-modules/main.js

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { h, render } from 'preact';
2+
3+
import App from './components/App';
4+
5+
render(<App />, document.getElementById('app'));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import './styles.css';
2+
import './styles.less';
3+
import './styles.scss';
4+
import './styles.stylus';
5+
import stylesCss from './styles.module.css?module';
6+
import stylesLess from './styles.module.less?module';
7+
import stylesScss from './styles.module.scss?module';
8+
import stylesStylus from './styles.module.stylus?module';
9+
10+
export default function App() {
11+
return <div className={`red large justified lowercase ${stylesCss.italic} ${stylesLess.underline} ${stylesScss.bold} ${stylesStylus.rtl}`}></div>
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.red {
2+
color: red;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.justified {
2+
text-align: justify;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.italic {
2+
font-style: italic;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.underline {
2+
text-decoration: underline;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.bold {
2+
font-weight: bold;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.rtl
2+
direction: rtl;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.large {
2+
font-size: 50px;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.lowercase
2+
text-transform: lowercase

fixtures/react-css-modules/main.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import {createRoot} from 'react-dom/client';
2+
import App from './components/App';
3+
4+
const root = createRoot(document.getElementById('app'));
5+
6+
root.render(<App />);

lib/loaders/babel.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,10 @@ module.exports = {
7070
if (webpackConfig.useReact) {
7171
loaderFeatures.ensurePackagesExistAndAreCorrectVersion('react');
7272

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

7679
if (webpackConfig.usePreact) {

package.json

+4-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
"@babel/eslint-parser": "^7.17.0",
4949
"@babel/plugin-transform-react-jsx": "^7.12.11",
5050
"@babel/preset-env": "^7.16.0",
51-
"@babel/preset-react": "^7.0.0",
51+
"@babel/preset-react": "^7.9.0",
5252
"@babel/preset-typescript": "^7.0.0",
5353
"@hotwired/stimulus": "^3.0.0",
5454
"@symfony/mock-module": "file:fixtures/stimulus/mock-module",
@@ -81,6 +81,8 @@
8181
"preact": "^10.5.0",
8282
"preact-compat": "^3.17.0",
8383
"puppeteer": "^23.2.2",
84+
"react": "^18.0.0",
85+
"react-dom": "^18.0.0",
8486
"sass": "^1.17.0",
8587
"sass-loader": "^16.0.1",
8688
"sinon": "^14.0.0",
@@ -102,7 +104,7 @@
102104
"@babel/core": "^7.17.0",
103105
"@babel/plugin-transform-react-jsx": "^7.12.11",
104106
"@babel/preset-env": "^7.16.0",
105-
"@babel/preset-react": "^7.0.0",
107+
"@babel/preset-react": "^7.9.0",
106108
"@babel/preset-typescript": "^7.0.0",
107109
"@symfony/stimulus-bridge": "^3.0.0",
108110
"@vue/babel-helper-vue-jsx-merge-props": "^1.0.0",

test/functional.js

+168-4
Original file line numberDiff line numberDiff line change
@@ -1684,13 +1684,179 @@ module.exports = {
16841684
expectClassDeclaration('large'); // Standard SCSS
16851685
expectClassDeclaration('justified'); // Standard Less
16861686
expectClassDeclaration('lowercase'); // Standard Stylus
1687-
expectClassDeclaration('block'); // Standard Postcss
1687+
expectClassDeclaration('block'); // Standard PostCSS
1688+
1689+
expectClassDeclaration('italic_foo'); // CSS Module
1690+
expectClassDeclaration('bold_foo'); // SCSS Module
1691+
expectClassDeclaration('underline_foo'); // Less Module
1692+
expectClassDeclaration('rtl_foo'); // Stylus Module
1693+
expectClassDeclaration('hidden_foo'); // PostCSS Module
1694+
1695+
testSetup.requestTestPage(
1696+
browser,
1697+
path.join(config.getContext(), 'www'),
1698+
[
1699+
'build/runtime.js',
1700+
'build/main.js'
1701+
],
1702+
async({ page }) => {
1703+
const divClassArray = await page.evaluate(() => Array.from(document.body.querySelector('#app > div').classList.values()));
1704+
1705+
expect(divClassArray.includes('red')).to.be.true; // Standard CSS
1706+
expect(divClassArray.includes('large')).to.be.true; // Standard SCSS
1707+
expect(divClassArray.includes('justified')).to.be.true; // Standard Less
1708+
expect(divClassArray.includes('lowercase')).to.be.true; // Standard Stylus
1709+
expect(divClassArray.includes('block')).to.be.true; // Standard PostCSS
1710+
1711+
expect(divClassArray.includes('italic_foo')).to.be.true; // CSS module
1712+
expect(divClassArray.includes('bold_foo')).to.be.true; // SCSS module
1713+
expect(divClassArray.includes('underline_foo')).to.be.true; // Less module
1714+
expect(divClassArray.includes('rtl_foo')).to.be.true; // Stylus module
1715+
expect(divClassArray.includes('hidden_foo')).to.be.true; // PostCSS module
1716+
1717+
done();
1718+
}
1719+
);
1720+
});
1721+
});
1722+
1723+
it('React supports CSS/Sass/Less/Stylus modules', (done) => {
1724+
const appDir = testSetup.createTestAppDir();
1725+
const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev');
1726+
config.enableSingleRuntimeChunk();
1727+
config.setPublicPath('/build');
1728+
config.addEntry('main', './react-css-modules/main.js');
1729+
config.enableReactPreset();
1730+
config.enableSassLoader();
1731+
config.enableLessLoader();
1732+
config.enableStylusLoader();
1733+
config.configureCssLoader(options => {
1734+
// Remove hashes from local ident names
1735+
// since they are not always the same.
1736+
if (options.modules) {
1737+
options.modules.localIdentName = '[local]_foo';
1738+
}
1739+
});
1740+
1741+
// Enable the PostCSS loader so we can use `lang="postcss"`
1742+
config.enablePostCssLoader();
1743+
fs.writeFileSync(
1744+
path.join(appDir, 'postcss.config.js'),
1745+
`
1746+
module.exports = {
1747+
plugins: [
1748+
require('autoprefixer')()
1749+
]
1750+
} `
1751+
);
1752+
1753+
testSetup.runWebpack(config, (webpackAssert) => {
1754+
expect(config.outputPath).to.be.a.directory().with.deep.files([
1755+
'main.js',
1756+
'main.css',
1757+
'manifest.json',
1758+
'entrypoints.json',
1759+
'runtime.js',
1760+
]);
1761+
1762+
const expectClassDeclaration = (className) => {
1763+
webpackAssert.assertOutputFileContains(
1764+
'main.css',
1765+
`.${className} {`
1766+
);
1767+
};
1768+
1769+
expectClassDeclaration('red'); // Standard CSS
1770+
expectClassDeclaration('large'); // Standard SCSS
1771+
expectClassDeclaration('justified'); // Standard Less
1772+
expectClassDeclaration('lowercase'); // Standard Stylus
1773+
1774+
expectClassDeclaration('italic_foo'); // CSS Module
1775+
expectClassDeclaration('bold_foo'); // SCSS Module
1776+
expectClassDeclaration('underline_foo'); // Less Module
1777+
expectClassDeclaration('rtl_foo'); // Stylus Module
1778+
1779+
testSetup.requestTestPage(
1780+
browser,
1781+
path.join(config.getContext(), 'www'),
1782+
[
1783+
'build/runtime.js',
1784+
'build/main.js'
1785+
],
1786+
async({ page }) => {
1787+
const divClassArray = await page.evaluate(() => Array.from(document.body.querySelector('#app > div').classList.values()));
1788+
1789+
expect(divClassArray.includes('red')).to.be.true; // Standard CSS
1790+
expect(divClassArray.includes('large')).to.be.true; // Standard SCSS
1791+
expect(divClassArray.includes('justified')).to.be.true; // Standard Less
1792+
expect(divClassArray.includes('lowercase')).to.be.true; // Standard Stylus
1793+
1794+
expect(divClassArray.includes('italic_foo')).to.be.true; // CSS module
1795+
expect(divClassArray.includes('bold_foo')).to.be.true; // SCSS module
1796+
expect(divClassArray.includes('underline_foo')).to.be.true; // Less module
1797+
expect(divClassArray.includes('rtl_foo')).to.be.true; // Stylus module
1798+
1799+
done();
1800+
}
1801+
);
1802+
});
1803+
});
1804+
1805+
it('Preact supports CSS/Sass/Less/Stylus modules', (done) => {
1806+
const appDir = testSetup.createTestAppDir();
1807+
const config = testSetup.createWebpackConfig(appDir, 'www/build', 'dev');
1808+
config.enableSingleRuntimeChunk();
1809+
config.setPublicPath('/build');
1810+
config.addEntry('main', './preact-css-modules/main.js');
1811+
config.enablePreactPreset();
1812+
config.enableSassLoader();
1813+
config.enableLessLoader();
1814+
config.enableStylusLoader();
1815+
config.configureCssLoader(options => {
1816+
// Remove hashes from local ident names
1817+
// since they are not always the same.
1818+
if (options.modules) {
1819+
options.modules.localIdentName = '[local]_foo';
1820+
}
1821+
});
1822+
1823+
// Enable the PostCSS loader so we can use `lang="postcss"`
1824+
config.enablePostCssLoader();
1825+
fs.writeFileSync(
1826+
path.join(appDir, 'postcss.config.js'),
1827+
`
1828+
module.exports = {
1829+
plugins: [
1830+
require('autoprefixer')()
1831+
]
1832+
} `
1833+
);
1834+
1835+
testSetup.runWebpack(config, (webpackAssert) => {
1836+
expect(config.outputPath).to.be.a.directory().with.deep.files([
1837+
'main.js',
1838+
'main.css',
1839+
'manifest.json',
1840+
'entrypoints.json',
1841+
'runtime.js',
1842+
]);
1843+
1844+
const expectClassDeclaration = (className) => {
1845+
webpackAssert.assertOutputFileContains(
1846+
'main.css',
1847+
`.${className} {`
1848+
);
1849+
};
1850+
1851+
expectClassDeclaration('red'); // Standard CSS
1852+
expectClassDeclaration('large'); // Standard SCSS
1853+
expectClassDeclaration('justified'); // Standard Less
1854+
expectClassDeclaration('lowercase'); // Standard Stylus
16881855

16891856
expectClassDeclaration('italic_foo'); // CSS Module
16901857
expectClassDeclaration('bold_foo'); // SCSS Module
16911858
expectClassDeclaration('underline_foo'); // Less Module
16921859
expectClassDeclaration('rtl_foo'); // Stylus Module
1693-
expectClassDeclaration('hidden_foo'); // Stylus Module
16941860

16951861
testSetup.requestTestPage(
16961862
browser,
@@ -1706,13 +1872,11 @@ module.exports = {
17061872
expect(divClassArray.includes('large')).to.be.true; // Standard SCSS
17071873
expect(divClassArray.includes('justified')).to.be.true; // Standard Less
17081874
expect(divClassArray.includes('lowercase')).to.be.true; // Standard Stylus
1709-
expect(divClassArray.includes('block')).to.be.true; // Standard Stylus
17101875

17111876
expect(divClassArray.includes('italic_foo')).to.be.true; // CSS module
17121877
expect(divClassArray.includes('bold_foo')).to.be.true; // SCSS module
17131878
expect(divClassArray.includes('underline_foo')).to.be.true; // Less module
17141879
expect(divClassArray.includes('rtl_foo')).to.be.true; // Stylus module
1715-
expect(divClassArray.includes('hidden_foo')).to.be.true; // Stylus module
17161880

17171881
done();
17181882
}

test/loaders/babel.js

+16-2
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,23 @@ describe('loaders/babel', () => {
7575

7676
// env, react & foo
7777
expect(actualLoaders[0].options.presets).to.have.lengthOf(3);
78-
expect(actualLoaders[0].options.presets).to.include(require.resolve('@babel/preset-react'));
78+
expect(actualLoaders[0].options.presets[0]).to.deep.equal([
79+
require.resolve('@babel/preset-env'),
80+
{
81+
corejs: null,
82+
modules: false,
83+
targets: {},
84+
useBuiltIns: false,
85+
},
86+
]);
87+
expect(actualLoaders[0].options.presets[1]).to.deep.equal([
88+
require.resolve('@babel/preset-react'),
89+
{
90+
runtime: 'automatic',
91+
}
92+
]);
7993
// foo is also still there, not overridden
80-
expect(actualLoaders[0].options.presets).to.include('foo');
94+
expect(actualLoaders[0].options.presets[2]).to.equal('foo');
8195
});
8296

8397
it('getLoaders() with preact', () => {

0 commit comments

Comments
 (0)