Skip to content

Commit

Permalink
Merge pull request #61 from mvdwg/add_build_features_command
Browse files Browse the repository at this point in the history
Add build:features command
  • Loading branch information
juanazam authored Mar 21, 2017
2 parents 0647f34 + cac2032 commit 4d46067
Show file tree
Hide file tree
Showing 10 changed files with 385 additions and 1 deletion.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,23 @@ By enabling it, your tests will run in "human" mode.

Code Corps [Video](https://www.youtube.com/watch?v=rzEEmkChYN8) | [Branch](https://github.com/mvdwg/code-corps-ember/tree/telling-stories)

## Generate documentation site

To generate a visual documentation site for your app, just run

```
$ ember build:features
```

This command generates a documentation site on dist/ folder. You can check it
out locally by running

```
$ cd dist/
$ python -m SimpleHTTPServer
```


## Development

```
Expand Down
34 changes: 34 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,28 @@ module.exports = {
if (!this.shouldIncludeFiles()) {
return;
}
this.isGeneratingSite = !!process.env['TELLING_STORIES'];

this._super.included.apply(this, arguments);

app.import('vendor/telling-stories/qunit-configuration.js', { type: 'test', prepend: true });
app.import('vendor/telling-stories/player-mode.css', { type: 'test' });
},

postBuild: function(result) {
if (!this.isGeneratingSite) {
return;
}

var dir = result.directory;
var fs = require('fs');
var path = require('path');

fs.unlinkSync(path.resolve(dir, 'index.html'));
fs.renameSync(path.resolve(dir, 'tests/index.html'), path.resolve(dir, 'runner.html'));
fs.renameSync(path.resolve(dir, 'telling-stories/viewer.html'), path.resolve(dir, 'index.html'));
},

treeFor: function() {
if (!this.shouldIncludeFiles()) {
return;
Expand All @@ -30,11 +45,30 @@ module.exports = {
return this._super.treeFor.apply(this, arguments);
},

treeForPublic: function() {
if (!this.isGeneratingSite) {
return;
}

var publicTree = this._super.treeForPublic.apply(this, arguments);

var CreateListOfTests = require('./lib/create-list-of-tests');
var testListTree = new CreateListOfTests('tests');

var BroccoliMergeTrees = require('broccoli-merge-trees');

return new BroccoliMergeTrees([publicTree, testListTree]);
},

isDevelopingAddon: function() {
return true;
},

shouldIncludeFiles: function() {
return !!this.app.tests;
},

includedCommands: function() {
return require('./lib/commands');
}
};
27 changes: 27 additions & 0 deletions lib/commands/build-features.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/* jshint node: true */
'use strict';

var Command = require('ember-cli/lib/models/command');

module.exports = Command.extend({
name: 'build:features',
description: 'Build visual documentation site',
works: 'insideProject',
availableOptions: [
{ name: 'output-path', type: 'Path', default: 'dist/', aliases: ['o'] }
],

run: function(commandOptions) {
var buildTask = new this.tasks.Build({
ui: this.ui,
analytics: this.analytics,
project: this.project
});

process.env['TELLING_STORIES'] = true;

return buildTask.run(Object.assign(commandOptions, {
environment: 'test'
}));
}
});
6 changes: 6 additions & 0 deletions lib/commands/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/* jshint node: true */
'use strict';

module.exports = {
'build:features': require('./build-features')
};
34 changes: 34 additions & 0 deletions lib/create-list-of-tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
var Plugin = require('broccoli-plugin');
var fs = require('fs');
var glob = require('glob');
var path = require('path');

module.exports = CreateListOfTests;

CreateListOfTests.prototype = Object.create(Plugin.prototype);
CreateListOfTests.prototype.constructor = CreateListOfTests;
function CreateListOfTests(inputNode, options) {
options = options || {};
Plugin.call(this, [inputNode], {
annotation: options.annotation
});
this.options = options;
}

CreateListOfTests.prototype.build = function() {
var extractMetadata = require('./extract-metadata');
var inputPath = this.inputPaths[0];

var records = glob.sync('**/*-test.js', { cwd: inputPath })
.map(function(fileName) {
return path.join(inputPath, fileName);
})
.map(function(file) {
return extractMetadata(fs.readFileSync(file));
})
.filter(function(entry) {
return !!entry;
});

fs.writeFileSync(path.join(this.outputPath, 'telling-stories.json'), JSON.stringify({ features: records }));
};
53 changes: 53 additions & 0 deletions lib/extract-metadata.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
var crypto = require('crypto');
var logger = require('heimdalljs-logger')('telling-stories-parser');

module.exports = extractMetadata;

function goodEnoughId(module, test) {
return crypto
.createHash('md5')
.update(module)
.update(test)
.digest('base64').replace(/\\\+|=/g,''); // remove \ + = chars
}

// I'm using functions to create a new instance each time the regexp is used.
// This is to avoid the state change provoked by the `.exec` method. Note that
// moduleTextRegexp doesn't uses the `g` modifier so it shouldn't have any
// problem but we might need to use the `g` modifier in the future so...
function moduleTextRegexp() {
return /moduleForAcceptance\((['"`])((?:.|\\')+)\1/;
}

function testRegexp() {
return /test\((['"`])((?:.|\\')+)\1.+\)/g
}

function extractMetadata(content, meta) {
var matches = moduleTextRegexp().exec(content);
var record;
var tests;
var re = testRegexp();

if (!matches) {
if (/moduleForAcceptance/.test(content)) {
logger.error("Wow, we couldn't parse the file " + meta.fileName + " correctly. We have a bug in the regexp");
}
} else if (matches[2]) {
record = {
module: matches[2],
tests: []
};

while ((matches = re.exec(content)) !== null) {
if (matches[2]) {
record.tests.push({
id: goodEnoughId(record.module, matches[2]),
name: matches[2]
});
}
}

return record;
}
}
36 changes: 36 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*jshint node:true*/
module.exports = {
name: 'telling-stories-parser',

included: function(app, addon) {
if (typeof app.import !== 'function' && app.app) {
app = app.app;
}

this.app = app;

this._super.included.apply(this, arguments);
},

isDevelopingAddon: function() {
return true;
},

treeFor(name) {
if (!this.isEnabled()) {
return;
}

return this._super.treeFor.apply(this, arguments);
},

treeForPublic() {
var CreateListOfTests = require('./create-list-of-tests');

return new CreateListOfTests('tests');
},

isEnabled: function() {
return this.app.env !== 'development';
}
};
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@
"test": "ember try:each"
},
"dependencies": {
"ember-cli-babel": "^5.1.7"
"broccoli-merge-trees": "^2.0.0",
"broccoli-plugin": "^1.3.0",
"ember-cli-babel": "^5.1.7",
"glob": "^7.1.1"
},
"devDependencies": {
"broccoli-asset-rev": "^2.4.5",
Expand Down
109 changes: 109 additions & 0 deletions public/app.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
html,
body,
.ember-application > .ember-view,
.ember-application > .ember-view > .ember-view {
height: 100%; margin: 0;
}

.ts {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #fff;
display: flex;
flex-flow: row;
height: 100%;
margin: 0;

color: rgb(102, 102, 102);
font-family: 'Roboto', 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
font-size: 14px;
font-weight: 300;
letter-spacing: .01em;
line-height: 1.6;
}

.ts-feature-list {
width: 220px;
border-right: 1px solid #666;
height: 100vh;
overflow: scroll;
}

.ts-header {
display: block;
color: #FAFAFA;
background-color: #333;
}

.ts-header h1 {
margin: 0;
text-align: center;
font-weight: 100;
}

.ts-feature-list__title {
font-size: 110%;
position: relative;
border-radius: 3px;
padding: 0.75em 1em;
margin: 0;
background-color: #FAFAFA;
box-shadow: 1px 1px 4px rgba(0,0,0,0.065);
font-weight: 100;
}

.ts-feature-item {
display: flex;
background-color: #FAFAFA;
background: linear-gradient(to bottom, #fff, #F2F2F2);
padding: 0.75em 1em;
box-shadow: 1px 1px 4px rgba(0,0,0,0.065);
position: relative;
padding: 10px 20px !important;
list-style: none;
box-sizing: border-box;
text-decoration: none;
cursor: pointer;
border-left: 5px solid #C6E746;
margin-bottom: 3px;
}

.ts-feature-item:hover,
.ts-feature-item:visited,
.ts-feature-item:active,
.ts-feature-item {
color:#666;
}

.ts-feature-item:hover,
.ts-feature-item.active {
background: linear-gradient(to bottom, #F2F2F2, #fff);
}

.ts iframe {
width: 100%;
border: none;
min-height: 400px;
flex: 1 1;
}

.ts-empty-state {
box-sizing: border-box;
width: calc(100% - 220px);
padding-top: 150px;
text-align: center;
font-size: 200%;
font-weight: bold;
color: white;
text-shadow: 0px 0px 2px black;

background: url('/telling-stories-dashboard/popcorn.png') 40% 25% no-repeat;
}

.ts-empty-state--message {
max-width: 450px;
display: inline-block;
}
Loading

0 comments on commit 4d46067

Please sign in to comment.