diff --git a/packages/cli/test/cases/develop.config.base-path/develop.config.base-path.spec.js b/packages/cli/test/cases/develop.config.base-path/develop.config.base-path.spec.js new file mode 100644 index 000000000..95596f459 --- /dev/null +++ b/packages/cli/test/cases/develop.config.base-path/develop.config.base-path.spec.js @@ -0,0 +1,301 @@ +/* + * Use Case + * Run Greenwood develop command with no config. + * + * User Result + * Should start the development server and render a bare bones Greenwood build. + * + * User Command + * greenwood develop + * + * User Config + * devServer: { + * basePath: '/my-path' + * } + * + * User Workspace + * src/ + * assets/ + * logo.png + * components/ + * card.js + * pages/ + * index.html + * styles/ + * main.css + * package.json + */ +import chai from 'chai'; +import { JSDOM } from 'jsdom'; +import path from 'path'; +import { getSetupFiles } from '../../../../../test/utils.js'; +import request from 'request'; +import { Runner } from 'gallinago'; +import { fileURLToPath, URL } from 'url'; + +const expect = chai.expect; + +describe('Develop Greenwood With: ', function() { + const LABEL = 'Base Path Configuration'; + const cliPath = path.join(process.cwd(), 'packages/cli/src/index.js'); + const outputPath = fileURLToPath(new URL('.', import.meta.url)); + const hostname = 'http://localhost'; + const port = 1984; + const basePath = '/my-path'; + let runner; + + before(function() { + this.context = { + hostname: `${hostname}:${port}` + }; + runner = new Runner(); + }); + + describe(LABEL, function() { + + before(async function() { + + await runner.setup(outputPath, [ + ...getSetupFiles(outputPath) + ]); + + return new Promise(async (resolve) => { + setTimeout(() => { + resolve(); + }, 5000); + + await runner.runCommand(cliPath, 'develop'); + }); + }); + + describe('Develop command specific HTML behaviors', function() { + let response = {}; + let dom; + + before(async function() { + return new Promise((resolve, reject) => { + request.get({ + url: `http://127.0.0.1:${port}${basePath}/`, + headers: { + accept: 'text/html' + } + }, (err, res, body) => { + if (err) { + reject(); + } + + response = res; + dom = new JSDOM(body); + + resolve(); + }); + }); + }); + + it('should return the correct content type', function(done) { + expect(response.headers['content-type']).to.contain('text/html'); + done(); + }); + + it('should return a 200', function(done) { + expect(response.statusCode).to.equal(200); + + done(); + }); + + it('should have the expected heading tag in the DOM', function(done) { + const headings = Array.from(dom.window.document.querySelectorAll('body > h1')); + + expect(headings.length).to.equal(1); + expect(headings[0].textContent).to.equal('Hello World'); + + done(); + }); + + it('should have the expected tag in the DOM', function(done) { + const cards = Array.from(dom.window.document.querySelectorAll('body > app-card')); + + expect(cards.length).to.equal(1); + + done(); + }); + }); + + describe('Develop command specific JavaScript behaviors for user authored custom element', function() { + let response = {}; + + before(async function() { + return new Promise((resolve, reject) => { + request.get(`${hostname}:${port}${basePath}/components/card.js`, (err, res, body) => { + if (err) { + reject(); + } + + response = res; + response.body = body; + + resolve(); + }); + }); + }); + + it('should return a 200 status', function(done) { + expect(response.statusCode).to.equal(200); + done(); + }); + + it('should return the correct content type', function(done) { + expect(response.headers['content-type']).to.contain('text/javascript'); + done(); + }); + + it('should return the correct response body', function(done) { + expect(response.body).to.contain('class Card extends HTMLElement'); + done(); + }); + }); + + describe('Develop command specific CSS behaviors', function() { + let response = {}; + + before(async function() { + return new Promise((resolve, reject) => { + request.get(`${hostname}:${port}${basePath}/styles/main.css`, (err, res, body) => { + if (err) { + reject(); + } + + response = res; + response.body = body; + + resolve(); + }); + }); + }); + + it('should eturn a 200 status', function(done) { + expect(response.statusCode).to.equal(200); + done(); + }); + + it('should return the correct content type', function(done) { + expect(response.headers['content-type']).to.contain('text/css'); + done(); + }); + + it('should return the correct response body', function(done) { + expect(response.body).to.contain('color: blue;'); + done(); + }); + }); + + describe('Develop command with image (png) specific behavior', function() { + const ext = 'png'; + let response = {}; + + before(async function() { + return new Promise((resolve, reject) => { + + request.get(`${hostname}:${port}${basePath}/assets/logo.${ext}`, (err, res, body) => { + if (err) { + reject(); + } + + response = res; + response.body = body; + + resolve(); + }); + }); + }); + + it('should return a 200 status', function(done) { + expect(response.statusCode).to.equal(200); + done(); + }); + + it('should return the correct content type', function(done) { + expect(response.headers['content-type']).to.contain(`image/${ext}`); + done(); + }); + + it('should return binary data', function(done) { + expect(response.body).to.contain('PNG'); + done(); + }); + }); + + // TODO + // proxies to https://jsonplaceholder.typicode.com/posts via greenwood.config.js + // describe('Develop command with dev proxy', function() { + // let response = {}; + + // before(async function() { + // return new Promise((resolve, reject) => { + // request.get(`${hostname}:${port}/posts?id=7`, (err, res, body) => { + // if (err) { + // reject(); + // } + + // response = res; + // response.body = JSON.parse(body); + + // resolve(); + // }); + // }); + // }); + + // it('should return a 200 status', function(done) { + // expect(response.statusCode).to.equal(200); + // done(); + // }); + + // it('should return the correct content type', function(done) { + // expect(response.headers['content-type']).to.contain('application/json'); + // done(); + // }); + + // it('should return the correct response body', function(done) { + // expect(response.body).to.have.lengthOf(1); + // done(); + // }); + // }); + + // TODO + // describe('Develop command with API specific behaviors', function() { + // const name = 'Greenwood'; + // let response = {}; + // let data = {}; + + // before(async function() { + // response = await fetch(`${hostname}:${port}/api/greeting?name=${name}`); + + // data = await response.json(); + // }); + + // it('should return a 200 status', function(done) { + // expect(response.ok).to.equal(true); + // expect(response.status).to.equal(200); + // done(); + // }); + + // it('should return the correct content type', function(done) { + // expect(response.headers.get('content-type')).to.equal('application/json; charset=utf-8'); + // done(); + // }); + + // it('should return the correct response body', function(done) { + // expect(data.message).to.equal(`Hello ${name}!!!`); + // done(); + // }); + // }); + }); + + after(function() { + runner.stopCommand(); + runner.teardown([ + path.join(outputPath, '.greenwood'), + path.join(outputPath, 'node_modules') + ]); + }); +}); \ No newline at end of file diff --git a/packages/cli/test/cases/develop.config.base-path/greenwood.config.js b/packages/cli/test/cases/develop.config.base-path/greenwood.config.js new file mode 100644 index 000000000..95cf3b707 --- /dev/null +++ b/packages/cli/test/cases/develop.config.base-path/greenwood.config.js @@ -0,0 +1,3 @@ +export default { + basePath: '/my-path' +}; \ No newline at end of file diff --git a/packages/cli/test/cases/develop.config.base-path/package.json b/packages/cli/test/cases/develop.config.base-path/package.json new file mode 100644 index 000000000..ab6239c28 --- /dev/null +++ b/packages/cli/test/cases/develop.config.base-path/package.json @@ -0,0 +1,4 @@ +{ + "name": "test-develop-command-config-base-path", + "type": "module" +} \ No newline at end of file diff --git a/packages/cli/test/cases/develop.config.base-path/src/assets/logo.png b/packages/cli/test/cases/develop.config.base-path/src/assets/logo.png new file mode 100644 index 000000000..786360832 Binary files /dev/null and b/packages/cli/test/cases/develop.config.base-path/src/assets/logo.png differ diff --git a/packages/cli/test/cases/develop.config.base-path/src/components/card.js b/packages/cli/test/cases/develop.config.base-path/src/components/card.js new file mode 100644 index 000000000..65a013b75 --- /dev/null +++ b/packages/cli/test/cases/develop.config.base-path/src/components/card.js @@ -0,0 +1,22 @@ +const template = document.createElement('template'); + +template.innerHTML = ` +
+ My default title + +
+
+`; + +class Card extends HTMLElement { + connectedCallback() { + if (!this.shadowRoot) { + this.attachShadow({ mode: 'open' }); + this.shadowRoot.appendChild(template.content.cloneNode(true)); + } + } +} + +customElements.define('app-card', Card); + +export default Card; \ No newline at end of file diff --git a/packages/cli/test/cases/develop.config.base-path/src/pages/index.html b/packages/cli/test/cases/develop.config.base-path/src/pages/index.html new file mode 100644 index 000000000..9b506b6cc --- /dev/null +++ b/packages/cli/test/cases/develop.config.base-path/src/pages/index.html @@ -0,0 +1,16 @@ + + + + + + + + +

Hello World

+ +

Greenwood Logo

+ Greenwood Logo +
+ + + \ No newline at end of file diff --git a/packages/cli/test/cases/develop.config.base-path/src/styles/main.css b/packages/cli/test/cases/develop.config.base-path/src/styles/main.css new file mode 100644 index 000000000..9a7b45f93 --- /dev/null +++ b/packages/cli/test/cases/develop.config.base-path/src/styles/main.css @@ -0,0 +1,3 @@ +* { + color: blue; +} \ No newline at end of file diff --git a/packages/cli/test/cases/serve.config.base-path/greenwood.config.js b/packages/cli/test/cases/serve.config.base-path/greenwood.config.js new file mode 100644 index 000000000..95cf3b707 --- /dev/null +++ b/packages/cli/test/cases/serve.config.base-path/greenwood.config.js @@ -0,0 +1,3 @@ +export default { + basePath: '/my-path' +}; \ No newline at end of file diff --git a/packages/cli/test/cases/serve.config.base-path/serve.config.base-path.spec.js b/packages/cli/test/cases/serve.config.base-path/serve.config.base-path.spec.js new file mode 100644 index 000000000..612df879e --- /dev/null +++ b/packages/cli/test/cases/serve.config.base-path/serve.config.base-path.spec.js @@ -0,0 +1,279 @@ +/* + * Use Case + * Run Greenwood serve command with no config. + * + * User Result + * Should start the production server and render a bare bones Greenwood build. + * + * User Command + * greenwood serve + * + * User Config + * devServer: { + * basePath: '/my-path' + * } + * + * User Workspace + * src/ + * assets/ + * logo.png + * components/ + * card.js + * pages/ + * index.html + * styles/ + * main.css + * package.json + */ +import chai from 'chai'; +import { JSDOM } from 'jsdom'; +import path from 'path'; +import { getSetupFiles, getOutputTeardownFiles } from '../../../../../test/utils.js'; +import request from 'request'; +import { Runner } from 'gallinago'; +import { fileURLToPath, URL } from 'url'; + +const expect = chai.expect; + +describe('Serve Greenwood With: ', function() { + const LABEL = 'Base Path Configuration'; + const cliPath = path.join(process.cwd(), 'packages/cli/src/index.js'); + const outputPath = fileURLToPath(new URL('.', import.meta.url)); + const hostname = 'http://127.0.0.1:8080'; + const basePath = '/my-path'; + const jsHash = '4bcc801e'; + const cssHash = '1454013616'; + let runner; + + before(function() { + this.context = { + hostname + }; + runner = new Runner(); + }); + + describe(LABEL, function() { + + before(async function() { + await runner.setup(outputPath, getSetupFiles(outputPath)); + await runner.runCommand(cliPath, 'build'); + + return new Promise(async (resolve) => { + setTimeout(() => { + resolve(); + }, 10000); + + await runner.runCommand(cliPath, 'serve'); + }); + }); + + describe('Serve command specific HTML behaviors', function() { + let response = {}; + let dom; + + before(async function() { + return new Promise((resolve, reject) => { + request.get({ + url: `${hostname}${basePath}/`, + headers: { + accept: 'text/html' + } + }, (err, res, body) => { + if (err) { + reject(); + } + + response = res; + dom = new JSDOM(body); + + resolve(); + }); + }); + }); + + it('should return the correct content type', function(done) { + expect(response.headers['content-type']).to.contain('text/html'); + done(); + }); + + it('should return a 200', function(done) { + expect(response.statusCode).to.equal(200); + + done(); + }); + + it('should have the expected heading tag in the DOM', function(done) { + const headings = Array.from(dom.window.document.querySelectorAll('body > h1')); + + expect(headings.length).to.equal(1); + expect(headings[0].textContent).to.equal('Hello World'); + + done(); + }); + + it('should have the expected tag in the DOM', function(done) { + const cards = Array.from(dom.window.document.querySelectorAll('body > app-card')); + + expect(cards.length).to.equal(1); + + done(); + }); + + it('should have the correct script link preload tag path in the DOM', function(done) { + const links = Array + .from(dom.window.document.querySelectorAll('head > link')) + .filter(link => link.getAttribute('as') === 'script'); + + expect(links.length).to.equal(1); + expect(links[0].getAttribute('href')).to.equal(`${basePath}/card.${jsHash}.js`); + + done(); + }); + + it('should have the correct script tag path in the DOM', function(done) { + const scripts = Array.from(dom.window.document.querySelectorAll('head > script')); + + expect(scripts.length).to.equal(1); + expect(scripts[0].getAttribute('src')).to.equal(`${basePath}/card.${jsHash}.js`); + + done(); + }); + + // + it('should have the correct style preload tag path in the DOM', function(done) { + const links = Array + .from(dom.window.document.querySelectorAll('head > link')) + .filter(link => link.getAttribute('as') === 'style'); + + expect(links.length).to.equal(1); + expect(links[0].getAttribute('href')).to.equal(`${basePath}/styles/main.${cssHash}.css`); + + done(); + }); + + it('should have the correct link tag for the stylesheet in the DOM', function(done) { + const styles = Array + .from(dom.window.document.querySelectorAll('head > link')) + .filter(link => link.getAttribute('rel') === 'stylesheet'); + + expect(styles.length).to.equal(1); + expect(styles[0].getAttribute('href')).to.equal(`${basePath}/styles/main.${cssHash}.css`); + + done(); + }); + }); + + describe('Serve command specific JavaScript behaviors for user authored custom element', function() { + let response = {}; + + before(async function() { + return new Promise((resolve, reject) => { + request.get(`${hostname}${basePath}/card.${jsHash}.js`, (err, res, body) => { + if (err) { + reject(); + } + + response = res; + response.body = body; + + resolve(); + }); + }); + }); + + it('should return a 200 status', function(done) { + expect(response.statusCode).to.equal(200); + done(); + }); + + it('should return the correct content type', function(done) { + expect(response.headers['content-type']).to.contain('text/javascript'); + done(); + }); + + it('should return the correct response body', function(done) { + expect(response.body).to.contain('class t extends HTMLElement'); + done(); + }); + }); + + describe('Serve command specific CSS behaviors', function() { + let response = {}; + + before(async function() { + return new Promise((resolve, reject) => { + request.get(`${hostname}${basePath}/styles/main.${cssHash}.css`, (err, res, body) => { + if (err) { + reject(); + } + + response = res; + response.body = body; + + resolve(); + }); + }); + }); + + it('should return a 200 status', function(done) { + expect(response.statusCode).to.equal(200); + done(); + }); + + it('should return the correct content type', function(done) { + expect(response.headers['content-type']).to.contain('text/css'); + done(); + }); + + it('should return the correct response body', function(done) { + expect(response.body).to.contain('*{color:blue}'); + done(); + }); + }); + + describe('Serve command with image (png) specific behavior', function() { + const ext = 'png'; + let response = {}; + + before(async function() { + return new Promise((resolve, reject) => { + + request.get(`${hostname}${basePath}/assets/logo.${ext}`, (err, res, body) => { + if (err) { + reject(); + } + + response = res; + response.body = body; + + resolve(); + }); + }); + }); + + it('should return a 200 status', function(done) { + expect(response.statusCode).to.equal(200); + done(); + }); + + it('should return the correct content type', function(done) { + expect(response.headers['content-type']).to.contain(`image/${ext}`); + done(); + }); + + it('should return binary data', function(done) { + expect(response.body).to.contain('PNG'); + done(); + }); + }); + + // TODO + // dev server proxy + // API end[points + }); + + after(function() { + runner.teardown(getOutputTeardownFiles(outputPath)); + runner.stopCommand(); + }); +}); \ No newline at end of file diff --git a/packages/cli/test/cases/serve.config.base-path/src/assets/logo.png b/packages/cli/test/cases/serve.config.base-path/src/assets/logo.png new file mode 100644 index 000000000..786360832 Binary files /dev/null and b/packages/cli/test/cases/serve.config.base-path/src/assets/logo.png differ diff --git a/packages/cli/test/cases/serve.config.base-path/src/components/card.js b/packages/cli/test/cases/serve.config.base-path/src/components/card.js new file mode 100644 index 000000000..65a013b75 --- /dev/null +++ b/packages/cli/test/cases/serve.config.base-path/src/components/card.js @@ -0,0 +1,22 @@ +const template = document.createElement('template'); + +template.innerHTML = ` +
+ My default title + +
+
+`; + +class Card extends HTMLElement { + connectedCallback() { + if (!this.shadowRoot) { + this.attachShadow({ mode: 'open' }); + this.shadowRoot.appendChild(template.content.cloneNode(true)); + } + } +} + +customElements.define('app-card', Card); + +export default Card; \ No newline at end of file diff --git a/packages/cli/test/cases/serve.config.base-path/src/pages/index.html b/packages/cli/test/cases/serve.config.base-path/src/pages/index.html new file mode 100644 index 000000000..9b506b6cc --- /dev/null +++ b/packages/cli/test/cases/serve.config.base-path/src/pages/index.html @@ -0,0 +1,16 @@ + + + + + + + + +

Hello World

+ +

Greenwood Logo

+ Greenwood Logo +
+ + + \ No newline at end of file diff --git a/packages/cli/test/cases/serve.config.base-path/src/styles/main.css b/packages/cli/test/cases/serve.config.base-path/src/styles/main.css new file mode 100644 index 000000000..9a7b45f93 --- /dev/null +++ b/packages/cli/test/cases/serve.config.base-path/src/styles/main.css @@ -0,0 +1,3 @@ +* { + color: blue; +} \ No newline at end of file