From c9f624b22e64f330c61e1162b5e30eea64aa6be2 Mon Sep 17 00:00:00 2001 From: Adam Putinski Date: Mon, 6 Jun 2016 14:47:01 -0700 Subject: [PATCH] Add build server --- .gitignore | 2 + .logs/test.txt | 23 +++ .travis.yml | 3 +- gulpfile.babel.js | 3 +- package.json | 13 +- scripts/gulp/generate-examples.js | 77 ++++++++++ scripts/gulp/generate-ui.js | 4 +- scripts/gulp/generate.js | 2 + scripts/helpers/paths.js | 4 +- scripts/helpers/publish.js | 138 ++++++++++++++++++ scripts/npm/build-server.js | 17 +++ scripts/npm/build-travis.js | 53 +++++++ .../site/assets.js => scripts/npm/test.js | 51 +++---- .../scripts/helpers/component-examples.js | 86 +++++++++++ 14 files changed, 438 insertions(+), 38 deletions(-) create mode 100644 .logs/test.txt create mode 100644 scripts/gulp/generate-examples.js create mode 100644 scripts/helpers/publish.js create mode 100644 scripts/npm/build-server.js create mode 100644 scripts/npm/build-travis.js rename test/integration/scripts/tasks/site/assets.js => scripts/npm/test.js (57%) create mode 100644 test/unit/scripts/helpers/component-examples.js diff --git a/.gitignore b/.gitignore index 36066977f6..659118fd3e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ .tmp /.dist /.npm +/.build +/.logs /.generated /.www /scripts/deploy diff --git a/.logs/test.txt b/.logs/test.txt new file mode 100644 index 0000000000..27080e5658 --- /dev/null +++ b/.logs/test.txt @@ -0,0 +1,23 @@ + +> design-system@2.0.0 test-unit /Users/adam.putinski/Development/github/design-system-internal +> mocha --compilers js:babel-register --require scripts/helpers/setup.js --reporter min 'test/unit/**/*.js' "--color" + + + 36 passing (241ms) + + +> design-system@2.0.0 test-integration /Users/adam.putinski/Development/github/design-system-internal +> mocha --compilers js:babel-register --require scripts/helpers/setup.js --reporter min 'test/integration/**/*.js' "--color" + + + 2 passing (430ms) + + +> design-system@2.0.0 test-browser /Users/adam.putinski/Development/github/design-system-internal +> karma start "--color" + +ERROR LOG: 'Warning: ReactDOMComponent: Do not access .props of a DOM node; instead, recreate the props as `render` did originally or read the DOM properties/attributes directly from this node (e.g., this.refs.box.className). This DOM node was rendered by `BreadCrumbs`.' + +Chrome 50.0.2661 (Mac OS X 10.11.5): Executed 3247 of 3247 SUCCESS (0.888 secs / 0.236 secs) +TOTAL: 3247 SUCCESS + diff --git a/.travis.yml b/.travis.yml index c33bf0da3c..0a1951bdb5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,8 +11,7 @@ install: - bundle install --path vendor/bundle - npm install script: - - npm run lint - - npm run test + - npm run build-travis after_success: - export SHA=`git rev-parse --short HEAD` - if [ -n $TRAVIS_PULL_REQUEST ] ; then curl https://$VISUALTEST_USERNAME:$VISUALTEST_PASSWORD@design-system-regression-test.herokuapp.com/ci/$SHA ; fi diff --git a/gulpfile.babel.js b/gulpfile.babel.js index 4c6c6e1a8b..bc664f07f3 100644 --- a/gulpfile.babel.js +++ b/gulpfile.babel.js @@ -157,7 +157,8 @@ gulp.task('clean', del.bind(null, [ __PATHS__.www, __PATHS__.generated, __PATHS__.tmp, - __PATHS__.dist + __PATHS__.dist, + __PATHS__.logs ])); gulp.task('serve', () => { diff --git a/package.json b/package.json index 1eff9c7176..1627d34233 100644 --- a/package.json +++ b/package.json @@ -23,14 +23,15 @@ }, "scripts": { "postinstall": "babel-node scripts/npm/postinstall.js", - "pretest": "npm run gulp -- build:test", - "test": "npm run test-unit && npm run test-browser && npm run test-integration", + "test": "babel-node scripts/npm/test.js", "test-unit": "mocha --compilers js:babel-register --require scripts/helpers/setup.js --reporter min 'test/unit/**/*.js'", "test-browser": "karma start", "test-integration": "mocha --compilers js:babel-register --require scripts/helpers/setup.js --reporter min 'test/integration/**/*.js'", "start": "npm run gulp", "build": "npm run gulp -- build", "build-prod": "npm run gulp -- build", + "build-server": "babel-node scripts/npm/build-server.js", + "build-travis": "babel-node scripts/npm/build-travis.js", "dist": "babel-node scripts/dist.js", "dist-npm": "babel-node scripts/dist.js --npm", "lint": "npm run gulp -- lint", @@ -38,7 +39,8 @@ "gulp": "gulp" }, "dependencies": { - "@salesforce-ux/design-tokens": "git+ssh://git@github.com:salesforce-ux/design-tokens-internal.git#v2.0.5", + "@salesforce-ux/build-server-api": "1.3.4", + "@salesforce-ux/design-tokens": "2.0.5", "@salesforce-ux/icons": "3.4.0", "app-module-path": "1.0.5", "async": "1.5.2", @@ -115,13 +117,16 @@ }, "devDependencies": { "chai": "3.5.0", + "cssstats": "^3.0.0-beta.1", "karma": "0.13.21", "karma-chai": "0.1.0", "karma-chrome-launcher": "0.2.2", "karma-mocha": "0.2.2", "karma-spec-reporter": "0.0.24", "karma-webpack": "1.7.0", + "mkdirp": "^0.5.1", "mocha": "2.4.5", - "sinon": "1.17.3" + "sinon": "1.17.3", + "strip-ansi": "^3.0.1" } } diff --git a/scripts/gulp/generate-examples.js b/scripts/gulp/generate-examples.js new file mode 100644 index 0000000000..fc4014f4e1 --- /dev/null +++ b/scripts/gulp/generate-examples.js @@ -0,0 +1,77 @@ +/* +Copyright (c) 2015, salesforce.com, inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +import fs from 'fs'; +import gulp from 'gulp'; +import gutil from 'gulp-util'; +import path from 'path'; +import through from 'through2'; +import beautify from 'js-beautify'; +import { renderToStaticMarkup } from 'react-dom/server'; + +import { resolve } from 'path'; +import { assign, flatMap } from 'lodash'; +import { generateUI } from './generate-ui'; + +const prettyHTML = html => beautify.html(html, { + 'indent_size': 2, + 'indent_char': ' ', + 'unformatted': ['a'], + 'wrap_line_length ': 78, + 'indent_inner_html': true +}); + +const toStates = (ex, k) => + [{ id: k, element: ex[k] }]; + +const getStates = file => + file.preview ? toStates(file, 'preview') + : file.default ? toStates(file, 'default') + : file.states; + +const requireExample = flavor => + require(resolve(__PATHS__.ui, flavor.examplePath)); + +export const addExamples = comps => + comps.map(c => assign(c, { + flavors: c.flavors + .filter(f => f.examplePath) + .map(f => assign(f, { examples: getStates(requireExample(f)) })) + }) +); + +export const getComponents = schema => + flatMap(generateUI(schema), 'components'); + +const getName = (component, flavor, example) => + `${component.id}_${flavor.id}_${example.id}`; + +const flattenExamples = comps => + flatMap(comps, comp => + flatMap(comp.flavors, flavor => + flatMap(flavor.examples, ex => + assign(ex, { uid: getName(comp, flavor, ex) })))); + +const toHtml = el => + prettyHTML(renderToStaticMarkup(el)); + +gulp.task('generate:examples', ['generate:whitelist'], () => { + const stream = through.obj(); + const examples = flattenExamples(addExamples(getComponents())); + examples.forEach(ex => + stream.write(new gutil.File({ + path: `${ex.uid}.html`, + contents: new Buffer(toHtml(ex.element)) + }))); + stream.end(); + return stream + .pipe(gulp.dest(resolve(__PATHS__.generated, 'examples'))); +}); diff --git a/scripts/gulp/generate-ui.js b/scripts/gulp/generate-ui.js index 8f29574fa0..b6428465ae 100644 --- a/scripts/gulp/generate-ui.js +++ b/scripts/gulp/generate-ui.js @@ -41,8 +41,8 @@ export const addExamplePath = scheme => }); }); -export const generateUI = () => { - const scheme = Scheme({ path: __PATHS__.ui }).generate(); +export const generateUI = (scheme) => { + scheme = scheme || Scheme({ path: __PATHS__.ui }).generate(); addStatus(scheme); addExamplePath(scheme); return scheme; diff --git a/scripts/gulp/generate.js b/scripts/gulp/generate.js index 779d449c4d..915578bd3b 100644 --- a/scripts/gulp/generate.js +++ b/scripts/gulp/generate.js @@ -16,6 +16,7 @@ import './generate-release-notes'; import './generate-tokens-zip'; import './generate-tokens'; import './generate-ui'; +import './generate-examples'; import './generate-whitelist'; gulp.task('generate', [ @@ -24,6 +25,7 @@ gulp.task('generate', [ 'generate:tokens:zip', 'generate:tokens', 'generate:ui', + 'generate:examples', 'generate:whitelist', 'generate:whitelist-utilities' ]); diff --git a/scripts/helpers/paths.js b/scripts/helpers/paths.js index 2478459fe4..670a898452 100644 --- a/scripts/helpers/paths.js +++ b/scripts/helpers/paths.js @@ -36,10 +36,12 @@ const paths = { dist: path.resolve(root, '.dist'), npm: path.resolve(root, '.npm'), + build: path.resolve(root, '.build'), generated: path.resolve(root, '.generated'), tmp: path.resolve(root, '.tmp'), test: path.resolve(root, '.test'), - www: path.resolve(root, '.www') + www: path.resolve(root, '.www'), + logs: path.resolve(root, '.logs') }; export default { diff --git a/scripts/helpers/publish.js b/scripts/helpers/publish.js new file mode 100644 index 0000000000..2efb6ef5f8 --- /dev/null +++ b/scripts/helpers/publish.js @@ -0,0 +1,138 @@ +/* +Copyright (c) 2015, salesforce.com, inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +import './setup'; +import _ from 'lodash'; +import async from 'async'; +import cssstats from 'cssstats'; +import { exec } from 'child_process'; +import fs from 'fs'; +import gulp from 'gulp'; +import gulpzip from 'gulp-zip'; +import path from 'path'; +import buildServerApi from '@salesforce-ux/build-server-api'; + +const { publish } = buildServerApi(process.env.BUILD_SERVER_HOST_NEW); + +const packageJSON = require('../../package.json'); + +const CSS_PATH = 'assets/styles/salesforce-lightning-design-system.css'; + +const paths = { + build: path.resolve.bind(path, __PATHS__.build), + buildDist: path.resolve.bind(path, __PATHS__.build, 'dist') +}; + +const execute = (cmd, done) => + exec(cmd, { cwd: __PATHS__.root, stdio: 'inherit' }, (err, out, stderr) => { + if (err) return done(err); + done(null, out.trim()); + }); + +const getDependencies = (done) => { + const deps = _.assign({}, + packageJSON.devDependencies, packageJSON.dependencies); + done(null, _.keys(deps) + .filter(k => k.match(/@salesforce/i)) + .reduce((acc, k) => + _.assign(acc, { [k.split('/')[1]]: deps[k] }), {})); +}; + +const formatStats = stats => ({ + kbSize: stats.size, + gzipSize: stats.gzipSize, + ruleCount: stats.rules.total, + selectorCount: stats.selectors.total, + declarationCount: stats.declarations.total +}); + +const formatTestCounts = out => { + const matches = out.match(/(\d+)\s+(SUCCESS|passing)/ig); + if (!matches) return {}; + return { + unitTests: parseInt(matches[0]), + integrationTests: parseInt(matches[1]), + allyTests: parseInt(matches[2]) + }; +}; + +const zip = (src, done) => + gulp.src(paths.build(`${src}/**/*`)) + .pipe(gulpzip(`${src}.zip`)) + .on('error', done) + .pipe(gulp.dest(paths.build())) + .on('error', done) + .on('finish', done); + +const prepare = (done) => { + console.log('[BUILD:PREPARE]'); + return async.series([ + // clean + (done) => async.series([ + async.apply(execute, `rm -rf ${__PATHS__.build}`), + async.apply(execute, `mkdir ${__PATHS__.build}`) + ], done), + // dist + (done) => async.series([ + async.apply(execute, 'npm run dist-npm'), + async.apply(execute, `cp -a ${__PATHS__.npm}/. ${__PATHS__.build}/dist`), + async.apply(execute, `rm -rf ${__PATHS__.build}/dist/*.zip`) + ], done), + // examples + async.apply(execute, `cp -a ${__PATHS__.generated}/examples/. ${__PATHS__.build}/examples`), + // website + async.apply(execute, `cp -a ${__PATHS__.www}/. ${__PATHS__.build}/www`), + // git info + async.apply(execute, 'git show --format="%an|%ae|%ad|%s" | head -n 1'), + // stats + (done) => async.series([ + (done) => { + let counts = fs.readFileSync(`${__PATHS__.logs}/test.txt`, 'utf-8') || ''; + done(null, formatTestCounts(counts)); + }, + (done) => { + let css = fs.readFileSync(paths.buildDist(CSS_PATH), 'utf8'); + let stats = cssstats(css); + done(null, formatStats(stats)); + } + ], (err, [counts, tests]) => { + if (err) return done(err); + done(null, _.assign({}, counts, tests)); + }), + // SHA + async.apply(execute, 'git rev-parse HEAD'), + // Dependencies + getDependencies, + // zip + async.apply(zip, 'dist'), + async.apply(zip, 'examples'), + async.apply(zip, 'www') + ], (err, [_prepare, _dist, _examples, _website, info, stats, sha, dependencies, _zip]) => { + if (err) return done(err); + let result = _.assign({}, { sha, info, stats, dependencies }, { + tag: process.env.TRAVIS_TAG || '', + pullRequest: process.env.TRAVIS_PULL_REQUEST || '', + branch: process.env.TRAVIS_BRANCH || '', + version: packageJSON.version + }); + done(null, result); + }); +}; + +module.exports = (done) => prepare((err, result) => { + if (err) return done(err); + publish({ + result, + zips: ['dist.zip', 'examples.zip', 'www.zip'] + .map((p) => paths.build(p)), + project: 'design-system' + }, done); +}); diff --git a/scripts/npm/build-server.js b/scripts/npm/build-server.js new file mode 100644 index 0000000000..c5d118e0bd --- /dev/null +++ b/scripts/npm/build-server.js @@ -0,0 +1,17 @@ +/* +Copyright (c) 2015, salesforce.com, inc. All rights reserved. +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +import publish from '../helpers/publish'; + +if (process.env.BUILD_SERVER_HOST_NEW) { + publish((err, res) => { + if (err) throw err; + console.log('Successfully published build', res); + }); +} diff --git a/scripts/npm/build-travis.js b/scripts/npm/build-travis.js new file mode 100644 index 0000000000..3b3f3466e5 --- /dev/null +++ b/scripts/npm/build-travis.js @@ -0,0 +1,53 @@ +/* +Copyright (c) 2015, salesforce.com, inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +import '../helpers/setup'; +import path from 'path'; +import { execSync } from 'child_process'; + +const local = path.resolve.bind(path, __dirname, '../../'); + +const exec = (command, cwd = '') => + execSync(command, { + cwd: local(cwd), + stdio: 'inherit', + env: Object.assign({}, process.env) + }); + +const setEnvironment = () => { + // Once the package.json is loaded into process.env all values become strings + if (process.env.npm_package_config_slds_internal === 'true') { + process.env.INTERNAL = process.env.npm_package_config_slds_internal; + process.env.INTERNAL_RELEASE_NAME = process.env.npm_package_slds_id; + process.env.SLDS_VERSION = `${process.env.npm_package_version} (${process.env.INTERNAL_RELEASE_NAME})`; + } else { + // If the value isn't "true", then delete the variables + // because the code checks like: + // if (process.env.INTENRAL) + delete process.env.INTERNAL; + delete process.env.INTERNAL_RELEASE_NAME; + process.env.SLDS_VERSION = `${process.env.npm_package_version}`; + } +}; + +const runScript = () => + exec('npm run build && npm run test && npm run lint'); + +const publishBuild = () => + exec('npm run build-server'); + +if (process.env.BUILD_SERVER_HOST_NEW) { + setEnvironment(); + runScript(); + publishBuild(); +} else { + runScript(); +}; diff --git a/test/integration/scripts/tasks/site/assets.js b/scripts/npm/test.js similarity index 57% rename from test/integration/scripts/tasks/site/assets.js rename to scripts/npm/test.js index 16af61f6ce..08ec09798b 100644 --- a/test/integration/scripts/tasks/site/assets.js +++ b/scripts/npm/test.js @@ -8,39 +8,34 @@ Neither the name of salesforce.com, inc. nor the names of its contributors may b THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -import { expect } from 'chai'; -import _ from 'lodash'; + +import '../helpers/setup'; + +import async from 'async'; +import { spawn } from 'child_process'; import fs from 'fs'; -import glob from 'glob'; +import mkdirp from 'mkdirp'; import path from 'path'; -import { ignore as ignoreExtensions } from 'scripts/gulp/assets'; +import stripAnsi from 'strip-ansi'; -describe('scripts/tasks/site/assets.js', () => { +mkdirp(__PATHS__.logs); - it('does not copy any jsx/scss/.file to .www', () => { - let files = glob.sync(`${__PATHS__.www}/**/*.{${ignoreExtensions}}`); - expect(files).to.eql([]); +let logs = ''; +let run = (script, done) => { + let command = spawn('npm', ['run', script, '--', '--color'], { + cwd: __PATHS__.root, + stdio: ['inherit', 'pipe', 'inherit'] }); - - it('does copy all non-jsx/scss to .www', () => { - - // build list of source site file paths which should be copied - let siteFiles = (function () { - let all = glob.sync(`${__PATHS__.site}/**/*.*`); - let ignore = glob.sync(`${__PATHS__.site}/**/*.{${ignoreExtensions}}`); - return _.difference(all, ignore); - })(); - - let relativeSiteFiles = siteFiles.map(f => path.relative(__PATHS__.site, f)); - - // build list of www file paths - let wwwFiles = glob.sync(`${__PATHS__.www}/**/*.*`); - let relativeWwwFiles = wwwFiles.map(f => path.relative(__PATHS__.www, f)); - - // all of relativeSiteFiles should be in relativeWwwFiles - let copiedSiteFiles = _.intersection(relativeSiteFiles, relativeWwwFiles); - expect(relativeSiteFiles).to.eql(copiedSiteFiles); + command.stdout.on('data', (d) => { + let str = d.toString(); + logs += stripAnsi(str); + console.log(str); }); + command.on('close', done); + command.on('error', done); +}; +async.eachSeries(['test-unit', 'test-integration', 'test-browser'], run, (err) => { + fs.writeFileSync(`${__PATHS__.logs}/test.txt`, logs); + if (err) throw new Error('Tests Failed (see output above)'); }); - diff --git a/test/unit/scripts/helpers/component-examples.js b/test/unit/scripts/helpers/component-examples.js new file mode 100644 index 0000000000..9154a81fa6 --- /dev/null +++ b/test/unit/scripts/helpers/component-examples.js @@ -0,0 +1,86 @@ +/* +Copyright (c) 2015, salesforce.com, inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +import { expect } from 'chai'; +import { isValidElement } from 'react'; +import { getComponents, addExamples } from 'scripts/gulp/generate-examples'; + +// example schema (slimmed down from actual) +const schema = [ + { id: "components", + path: "components", + components: [ + { id: "button", + path: "components/buttons", + status: "prototype", + flavors: [{ id: "base", path: "components/buttons/flavors/base", status: "prototype" }] + } + ] + }, + { id: "utilities", + path: "utilities", + components: [ + { id: "floats", + path: "utilities/floats", + status: "prototype", + flavors: [ + { id: "float-left", path:"utilities/floats/flavors/float-left", status:"dev-ready" }, + { id: "float-right", path:"BOOOOGGGGUUUSSSSS", classBase:"floats"} + ] + } + ] + } +] + + +describe('scripts/helpers/component-examples.js', () => { + describe('getComponents', () => { + let comps; + + before(() => { + comps = getComponents(schema) + }); + + it('returns an array of components and utils mixed together', () => + expect(comps.length).to.equal(2)); + + it('adds example paths to the components', () => + expect(comps[0].flavors[0].examplePath).to.include("components/buttons/flavors/base/index.react.example.jsx")); + + it('updates the status when prototype', () => + expect(comps[0].status).to.equal('prototype')) + + it('updates the status to dev-ready if any flavors are dev-ready', () => + expect(comps[1].status).to.equal('dev-ready')) + + describe('all html examples', () => { + let button, floats; + + before(() => { + [button, floats] = addExamples(getComponents(schema)) + }); + + it('adds example objects to the flavors with state', () => + expect(button.flavors[0].examples[0].id).to.equal('button')) + + it('adds example objects to the flavors w/o state', () => + expect(floats.flavors[0].examples[0].id).to.equal('default')) + + it('includes the element in the example object', () => + expect(isValidElement(button.flavors[0].examples[0].element)).to.be.ok) + + it('returns an empty array if no examples', () => + expect(floats.flavors.length).to.equal(1)) + }); + }); +}); + +