From 7adc1c0cdb46ed0cbea1804a7f142f672adb0320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Lorber?= Date: Thu, 30 Dec 2021 14:26:26 +0100 Subject: [PATCH] test: ensure consistent CSS ordering (#6222) --- website/_dogfooding/clientModuleCSS.css | 11 +++ website/_dogfooding/dogfooding.config.js | 12 +++ website/docusaurus.config.js | 1 - website/package.json | 5 +- website/src/css/custom.css | 8 ++ website/src/pages/styles.module.css | 5 + website/src/theme/Layout/index.tsx | 17 ++++ website/src/theme/Layout/styles.module.css | 12 +++ website/testCSSOrder.js | 110 +++++++++++++++++++++ 9 files changed, 178 insertions(+), 3 deletions(-) create mode 100644 website/_dogfooding/clientModuleCSS.css create mode 100644 website/src/theme/Layout/index.tsx create mode 100644 website/src/theme/Layout/styles.module.css create mode 100644 website/testCSSOrder.js diff --git a/website/_dogfooding/clientModuleCSS.css b/website/_dogfooding/clientModuleCSS.css new file mode 100644 index 000000000000..f67bf88354bc --- /dev/null +++ b/website/_dogfooding/clientModuleCSS.css @@ -0,0 +1,11 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/* Used to test CSS insertion order */ +.test-marker-site-client-module { + content: "site-client-module"; +} diff --git a/website/_dogfooding/dogfooding.config.js b/website/_dogfooding/dogfooding.config.js index 89eb42620bba..84832cbfc935 100644 --- a/website/_dogfooding/dogfooding.config.js +++ b/website/_dogfooding/dogfooding.config.js @@ -55,6 +55,18 @@ const dogfoodingPluginInstances = [ routeBasePath: '/tests/pages', }), ], + + /** @type {import('@docusaurus/types').Plugin} */ + function clientModuleTestPlugin() { + return { + getClientModules() { + return [ + require.resolve('./clientModuleExample.ts'), + require.resolve('./clientModuleCSS.css'), + ]; + }, + }; + }, ]; exports.dogfoodingPluginInstances = dogfoodingPluginInstances; diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 97fa7cb81e49..3a7771fcac38 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -118,7 +118,6 @@ const config = { 'static', path.join(__dirname, '_dogfooding/_asset-tests'), ], - clientModules: [require.resolve('./_dogfooding/clientModuleExample.ts')], themes: ['live-codeblock'], plugins: [ FeatureRequestsPlugin, diff --git a/website/package.json b/website/package.json index fe12088a585c..b238649abb58 100644 --- a/website/package.json +++ b/website/package.json @@ -10,6 +10,7 @@ "deploy": "docusaurus deploy", "clear": "docusaurus clear", "serve": "docusaurus serve", + "test:css-order": "node testCSSOrder.js", "write-translations": "docusaurus write-translations", "write-heading-ids": "docusaurus write-heading-ids", "start:baseUrl": "cross-env BASE_URL='/build/' yarn start", @@ -17,8 +18,8 @@ "start:blogOnly": "cross-env yarn start --config=docusaurus.config-blog-only.js", "build:blogOnly": "cross-env yarn build --config=docusaurus.config-blog-only.js", "build:fast": "cross-env BUILD_FAST=true yarn build --locale en", - "netlify:build:production": "yarn docusaurus write-translations && yarn netlify:crowdin:delay && yarn netlify:crowdin:uploadSources && yarn netlify:crowdin:downloadTranslations && yarn build", - "netlify:build:deployPreview": "yarn docusaurus write-translations --locale fr --messagePrefix '(fr) ' && yarn build", + "netlify:build:production": "yarn docusaurus write-translations && yarn netlify:crowdin:delay && yarn netlify:crowdin:uploadSources && yarn netlify:crowdin:downloadTranslations && yarn build && yarn test:css-order", + "netlify:build:deployPreview": "yarn docusaurus write-translations --locale fr --messagePrefix '(fr) ' && yarn build && yarn test:css-order", "netlify:crowdin:delay": "node delayCrowdin.js", "netlify:crowdin:wait": "node waitForCrowdin.js", "netlify:crowdin:downloadTranslations": "yarn netlify:crowdin:wait && yarn --cwd .. crowdin:download:website", diff --git a/website/src/css/custom.css b/website/src/css/custom.css index 38afeb295fc0..a19f239fcab7 100644 --- a/website/src/css/custom.css +++ b/website/src/css/custom.css @@ -171,3 +171,11 @@ div[class^='announcementBar_'] { width: 1px; white-space: nowrap; } + +/* Used to test CSS insertion order */ +.test-marker-site-custom-css-unique-rule { + content: "site-custom-css-unique-rule"; +} +.test-marker-site-custom-css-shared-rule { + max-width: 100%; +} diff --git a/website/src/pages/styles.module.css b/website/src/pages/styles.module.css index 3366f9779c8e..76c8e98728cc 100644 --- a/website/src/pages/styles.module.css +++ b/website/src/pages/styles.module.css @@ -153,3 +153,8 @@ display: none; } } + +/* Used to test CSS insertion order */ +.test-marker-site-index-page { + content: "site-index-page"; +} diff --git a/website/src/theme/Layout/index.tsx b/website/src/theme/Layout/index.tsx new file mode 100644 index 000000000000..78f03a793f88 --- /dev/null +++ b/website/src/theme/Layout/index.tsx @@ -0,0 +1,17 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import type {Props} from '@theme/Layout'; +import Layout from '@theme-original/Layout'; + +// This component is only used to test for CSS insertion order +import './styles.module.css'; + +export default function LayoutWrapper(props: Props): JSX.Element { + return ; +} diff --git a/website/src/theme/Layout/styles.module.css b/website/src/theme/Layout/styles.module.css new file mode 100644 index 000000000000..c3c0ab8bdb06 --- /dev/null +++ b/website/src/theme/Layout/styles.module.css @@ -0,0 +1,12 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/* Used to test CSS insertion order */ +.test-marker-theme-layout { + content: "theme-layout"; +} + diff --git a/website/testCSSOrder.js b/website/testCSSOrder.js new file mode 100644 index 000000000000..6acee5b7f1d5 --- /dev/null +++ b/website/testCSSOrder.js @@ -0,0 +1,110 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const path = require('path'); +const fs = require('fs'); + +/* +This verifies CSS ordering on the Docusaurus site itself, + +There are multiple ways to provide some CSS to Docusaurus +and Docusaurus should guarantee a consistent CSS ordering over time + +See also +- https://github.com/facebook/docusaurus/issues/3678 +- https://github.com/facebook/docusaurus/pull/5987 + +TODO we should probably add a real e2e test in core instead of using our own website? +Current solution looks good-enough for now + + */ + +// TODO temporary, the current order is bad and we should change/fix that +const EXPECTED_CSS_MARKERS = [ + // Note, Infima and site classes are optimized/deduplicated and put at the top + // We don't agree yet on what should be the order for those classes + // See https://github.com/facebook/docusaurus/pull/6222 + '.markdown>h2', + '.button--outline.button--active', + '.DocSearch-Hit-content-wrapper', + '.navbar__title', + '--ifm-color-scheme:light', + '.test-marker-site-custom-css-shared-rule', + '.col[class*=col--]', + '.padding-vert--xl', + '.footer__link-item', + '.pagination__item', + '.pills__item', + '.tabs__item', + + // Test markers + '.test-marker-site-custom-css-unique-rule', + '.test-marker-site-client-module', + '.test-marker-theme-layout', + '.test-marker-site-index-page', + + // lazy loaded lib + '.DocSearch-Modal', +]; + +const cssDirName = path.join(__dirname, 'build', 'assets', 'css'); + +const cssFileNames = fs + .readdirSync(cssDirName) + .filter((file) => file.endsWith('.css')); + +if (cssFileNames.length !== 1) { + throw new Error('unexpected: more than 1 css file'); +} +const cssFile = path.join(cssDirName, cssFileNames[0]); + +console.log('Inspecting CSS file for test CSS markers', cssFile); + +const cssFileContent = fs.readFileSync(cssFile, 'utf8'); + +const cssMarkersWithPositions = EXPECTED_CSS_MARKERS.map((marker) => { + const position = cssFileContent.indexOf(marker); + return {marker, position}; +}); + +const missingCSSMarkers = cssMarkersWithPositions + .filter((m) => m.position === -1) + .map((m) => m.marker); + +if (missingCSSMarkers.length > 0) { + throw new Error( + `Some expected CSS marker classes could not be found in file ${cssFile}: \n- ${missingCSSMarkers.join( + '\n- ', + )}`, + ); +} + +// https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore#_sortby-and-_orderby +const sortBy = (key) => (a, b) => + // eslint-disable-next-line no-nested-ternary + a[key] > b[key] ? 1 : b[key] > a[key] ? -1 : 0; + +const sortedCSSMarkers = cssMarkersWithPositions + .concat() + .sort(sortBy('position')) + .map(({marker}) => marker); + +if (JSON.stringify(sortedCSSMarkers) === JSON.stringify(EXPECTED_CSS_MARKERS)) { + console.log(`Test CSS markers were found in the expected order: +- ${sortedCSSMarkers.join('\n- ')}`); +} else { + throw new Error(`Test CSS markers were found in an incorrect order. + +Expected order: +- ${EXPECTED_CSS_MARKERS.join('\n- ')}; + +Actual order: +- ${sortedCSSMarkers.join('\n- ')}; + +CSS file: ${cssFile} + `); +}