Skip to content

Commit

Permalink
Rfc/issue 134 global css (#137)
Browse files Browse the repository at this point in the history
* implemented global CSS support with style-loader

* unit tests

* unit testing
  • Loading branch information
thescientist13 authored Aug 8, 2019
1 parent bb5639f commit ca44424
Show file tree
Hide file tree
Showing 18 changed files with 287 additions and 34 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"pwa-helpers": "^0.9.1",
"redux": "^4.0.1",
"redux-thunk": "^2.3.0",
"style-loader": "^0.23.1",
"wc-markdown-loader": "^0.1.2",
"webpack": "^4.29.6",
"webpack-cli": "^3.3.0",
Expand Down
28 changes: 19 additions & 9 deletions packages/cli/config/webpack.config.common.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,18 @@ module.exports = ({ config, context }) => {
to: path.join(context.publicDir, 'assets')
}] : [];

return {
const commonCssLoaders = [
{ loader: 'css-loader' },
{ loader: 'postcss-loader', options:
{
config: {
path: path.join(__dirname)
}
}
}
];

return {
entry: {
index: path.join(context.scratchDir, 'app', 'app.js')
},
Expand Down Expand Up @@ -73,16 +83,16 @@ module.exports = ({ config, context }) => {
}
}, {
test: /\.css$/,
exclude: new RegExp(`${config.themeFile}`),
loaders: [
{ loader: 'css-to-string-loader' },
{ loader: 'css-loader' },
{ loader: 'postcss-loader', options:
{
config: {
path: path.join(__dirname)
}
}
}
...commonCssLoaders
]
}, {
test: new RegExp(`${config.themeFile}`),
loaders: [
{ loader: 'style-loader' },
...commonCssLoaders
]
}, {
test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
Expand Down
5 changes: 3 additions & 2 deletions packages/cli/lib/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ module.exports = async (url, label, route, outputDirectory) => {

const renderer = new Renderer(browser);
const result = await renderer.serialize(url);

const dom = new JSDOM(result.content);

// need url: https://github.com/jsdom/jsdom/issues/2005
const dom = new JSDOM(result.content, { url });
const html = dom.serialize();
const target = path.join(outputDirectory, route);

Expand Down
12 changes: 10 additions & 2 deletions packages/cli/lifecycles/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ let defaultConfig = {
},
publicPath: '/',
title: 'Greenwood App',
meta: []
meta: [],
themeFile: 'theme.css'
};

module.exports = readAndMergeConfig = async() => {
Expand All @@ -22,7 +23,7 @@ module.exports = readAndMergeConfig = async() => {

if (fs.existsSync(path.join(process.cwd(), 'greenwood.config.js'))) {
const userCfgFile = require(path.join(process.cwd(), 'greenwood.config.js'));
const { workspace, devServer, publicPath, title, meta } = userCfgFile;
const { workspace, devServer, publicPath, title, meta, themeFile } = userCfgFile;

// workspace validation
if (workspace) {
Expand Down Expand Up @@ -69,6 +70,13 @@ module.exports = readAndMergeConfig = async() => {
customConfig.meta = meta;
}

if (themeFile) {
if (typeof themeFile !== 'string' && themeFile.indexOf('.') < 1) {
reject(`Error: greenwood.config.js themeFile must be a valid filename. got ${themeFile} instead.`);
}
customConfig.themeFile = themeFile;
}

if (devServer && Object.keys(devServer).length > 0) {

if (devServer.host) {
Expand Down
11 changes: 0 additions & 11 deletions packages/cli/templates/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,6 @@
<script src="//cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/2.2.7/webcomponents-bundle.js"></script>

<%= htmlWebpackPlugin.options.spaIndexFallbackScript %>

<style>
* {
margin: 0;
padding: 0;
text-decoration: none;
}
html {
background-color: #f0f0f0;
}
</style>
</head>

<body>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Use Case
* Run Greenwood build command with a bad value for themeFile in a custom config.
*
* User Result
* Should throw an error.
*
* User Command
* greenwood build
*
* User Config
* {
* themeFile: '{}'
* }
*
* User Workspace
* Greenwood default
*/
const expect = require('chai').expect;
const TestBed = require('../../test-bed');

describe('Build Greenwood With: ', () => {
let setup;

before(async () => {
setup = new TestBed();
setup.setupTestBed(__dirname);
});

describe('Custom Configuration with a bad value for theme file', () => {
it('should throw an error that themeFile must be a filename', async () => {
try {
await setup.runGreenwoodCommand('build');
} catch (err) {
expect(err).to.contain('Error: greenwood.config.js themeFile must be a valid filename. got {} instead.');
}
});
});

after(function() {
setup.teardownTestBed();
});

});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
themeFile: '{}'
};
95 changes: 95 additions & 0 deletions test/cli/cases/build.config.theme/build.config.theme.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Use Case
* Run Greenwood with a custom themeFile file in config and default workspace with a page template.
*
* User Result
* Should generate a bare bones Greenwood build. (same as build.default.spec.js) with custom theme styles
*
* User Command
* greenwood build
*
* User Config
* {
* title: 'My Custom Greenwood App'
* }
*
* User Workspace
* Greenwood default
* src/
* templates/
* page-template.js
* styles/
* my-brand.css
*/
const fs = require('fs');
const { JSDOM } = require('jsdom');
const path = require('path');
const expect = require('chai').expect;
const runSmokeTest = require('../../smoke-test');
const TestBed = require('../../test-bed');

describe('Build Greenwood With: ', async function() {
const LABEL = 'Custom Theme Configuration and Default Workspace';
let setup;

before(async function() {
setup = new TestBed();

this.context = setup.setupTestBed(__dirname);
});

describe(LABEL, function() {
before(async function() {
await setup.runGreenwoodCommand('build');
});

runSmokeTest(['public', 'not-found', 'index'], LABEL);

describe('Theme Styled Page Template', function() {
let dom;

before(async function() {
dom = await JSDOM.fromFile(path.resolve(this.context.publicDir, 'index.html'));
});

it('should output a single index.html file using our custom styled page template', function() {
expect(fs.existsSync(path.join(this.context.publicDir, './index.html'))).to.be.true;
});

it('should have the expected font import', async function() {
const styles = '@import url(//fonts.googleapis.com/css?family=Roboto';
const styleTags = dom.window.document.querySelectorAll('head style');
let importCount = 0;

styleTags.forEach((tag) => {
if (tag.textContent.indexOf(styles) >= 0) {
importCount += 1;
}
});

expect(importCount).to.equal(1);
});

it('should have the expected font family', async function() {
const styles = 'body{font-family:Roboto,sans-serif}';
const styleTags = dom.window.document.querySelectorAll('head style');
let fontCount = 0;

styleTags.forEach((tag) => {
if (tag.textContent.indexOf(styles) >= 0) {
fontCount += 1;
}
});

expect(fontCount).to.equal(1);
});

});

});

after(function() {
setup.teardownTestBed();
});

});
3 changes: 3 additions & 0 deletions test/cli/cases/build.config.theme/greenwood.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
themeFile: 'my-brand.css'
};
5 changes: 5 additions & 0 deletions test/cli/cases/build.config.theme/src/styles/my-brand.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@import url('//fonts.googleapis.com/css?family=Roboto');

body {
font-family: 'Roboto', sans-serif;
}
20 changes: 20 additions & 0 deletions test/cli/cases/build.config.theme/src/templates/page-template.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { html, LitElement } from 'lit-element';
import '../styles/my-brand.css';
MDIMPORT;
METAIMPORT;
METADATA;

class PageTemplate extends LitElement {
render() {
return html`
METAELEMENT
<div class='wrapper'>
<div class='page-template content owen-test'>
<entry></entry>
</div>
</div>
`;
}
}

customElements.define('page-template', PageTemplate);
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* src/
* styles/
* style.css
* theme.css
* templates/
* page-template.js
*/
Expand All @@ -25,7 +26,7 @@ const { JSDOM } = require('jsdom');
const TestBed = require('../../test-bed');

describe('Build Greenwood With: ', function() {
const LABEL = 'Default Greenwood Configuration and Workspace w/Custom Style Page Template';
const LABEL = 'Default Greenwood Configuration and Workspace w/Custom Style and Theme Page Template';
let setup;

before(async function() {
Expand All @@ -40,8 +41,8 @@ describe('Build Greenwood With: ', function() {
});

runSmokeTest(['public', 'index', 'not-found', 'hello'], LABEL);

describe('Custom Styled Page Template', function() {

let dom;

before(async function() {
Expand All @@ -52,26 +53,64 @@ describe('Build Greenwood With: ', function() {
expect(fs.existsSync(path.join(this.context.publicDir, './index.html'))).to.be.true;
});

it('should have the specific style in the page template that we added as part of our custom style', async function() {
it('should have the color style for the .owen-test element in the page template that we added as part of our custom style', async function() {

const customElement = dom.window.document.querySelector('.owen-test');
const computedStyle = dom.window.getComputedStyle(customElement);

expect(computedStyle.color).to.equal('rgb(0, 0, 255)');
});

it('should have the specific style in the markdown that we added as part of our custom style', async function() {
it('should have the color styles for the h3 element that we defined as part of our custom style', async function() {

const customHeader = dom.window.document.querySelector('h3');
const computedStyle = dom.window.getComputedStyle(customHeader);

expect(computedStyle.color).to.equal('green');
});
});

describe('Theme Styled Page Template', function() {
let dom;

before(async function() {
dom = await JSDOM.fromFile(path.resolve(this.context.publicDir, 'index.html'));
});

it('should have the expected font import', async function() {
const styles = '@import url(//fonts.googleapis.com/css?family=Source+Sans+Pro&display=swap);';
const styleTags = dom.window.document.querySelectorAll('head style');
let importCount = 0;

styleTags.forEach((tag) => {
if (tag.textContent.indexOf(styles) >= 0) {
importCount += 1;
}
});

expect(importCount).to.equal(1);
});

it('should have the expected font family', async function() {
const styles = 'body{font-family:Source Sans Pro,sans-serif}';
const styleTags = dom.window.document.querySelectorAll('head style');
let fontCount = 0;

styleTags.forEach((tag) => {
if (tag.textContent.indexOf(styles) >= 0) {
fontCount += 1;
}
});

expect(fontCount).to.equal(1);
});

});

});

after(function() {
setup.teardownTestBed();
});

});
Loading

0 comments on commit ca44424

Please sign in to comment.