Skip to content

Commit 25ea43f

Browse files
authored
Add files via upload
1 parent 42c7579 commit 25ea43f

File tree

6 files changed

+353
-1
lines changed

6 files changed

+353
-1
lines changed

README.md

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,105 @@
1-
# tapered-js
1+
## Synopsis
2+
Easy inline test writing.
3+
tapered uses **Babel** and **Webpack** to generate **Tape** test files from test code written inline in comments
4+
5+
## Installation
6+
npm install tapered.js
7+
8+
## Setup
9+
#### 1. babelrc
10+
11+
Add the following lines to your .babelrc file.
12+
* "presets": ["env"],
13+
* "plugins": ["tapered.js/tapered-babel-plugin.js"]
14+
15+
#### 2. webpack.config
16+
Add these requires to the top of your webpack.config file:
17+
* const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
18+
* const tapered = require('tapered.js/tapered-webpack-plugin.js');
19+
20+
Add the following to your webpack's plugins array and specify the location of your test file e.g. _/tests/tape-test-sample.js_
21+
```javascript
22+
plugins : [
23+
new tapered(),
24+
new webpack.optimize.UglifyJsPlugin({
25+
extractComments: {
26+
condition: /dab/,
27+
filename: '_INSERT TEST FILE LOCATION HERE_'
28+
},
29+
}),
30+
],
31+
```
32+
#### 3. tapered-webpack-plugin.js
33+
Specify the _same_ desired test file location for the fields marked with _'INSERT TEST FILE LOCATION HERE'_ in the tapered.js folder inside your _node modules_ directory: **_/node_modules/tapered.js/tapered-webpack-plugin.js_**
34+
35+
const file = compilation.assets['INSERT DESIRED TEST FILE LOCATION HERE'];
36+
compilation.assets['INSERT DESIRED TEST FILE LOCATION HERE']
37+
38+
## Usage
39+
40+
#### Quick Start
41+
~ _test name_
42+
~a: _specify assertion_
43+
44+
#### Simple Unit Test Writing
45+
###### Let's write a test file for the code below:
46+
const demo = {};
47+
demo.add = function(a, b) {
48+
return a + b;
49+
}
50+
51+
###### We write our tapered tests in _**block**_ comments
52+
_/*_ %tapered _*/_
53+
54+
_/*_
55+
~add
56+
~a: demo.add(1, 2) equal 3 | should add numbers
57+
~a: demo.add(0, 1) notEqual 2 | should add numbers correctly
58+
_*/_
59+
60+
###### This is transpiled to Tape code as follows:
61+
const demo = require('/src/demo.js');
62+
63+
test('add', function (t) {
64+
t.equal(demo.add(1, 2), 3 , 'should add numbers');
65+
t.notEqual(demo.add(0, 1), 2 , 'should add numbers correctly');
66+
t.end();
67+
});
68+
69+
## API Reference
70+
71+
#### Key Reminders
72+
**_1. All tapered code must be written inside of _**block**_ comments._**
73+
**_2. Tests must always begin with a name._**
74+
75+
Syntax | Function
76+
------------ | -------------
77+
%tapered | requires the file for testing
78+
~ _name_ | define test name
79+
~ _variables_ | define variables
80+
~a: _assertions_ | define assertions
81+
%g | defines global variables accessible across the entire test file
82+
~x: | skips a test
83+
~o: | tests only that test
84+
~p: | specifies number of assertions to run per test
85+
86+
#### Assertions
87+
##### Components
88+
1. Expression e.g. _demo.multiply(1,2)_
89+
2. Assertion e.g. _equal_
90+
3. Expected e.g. _2_
91+
4. Description/Message e.g. _should multiply numbers_
92+
93+
##### Format
94+
**_~a: _Expression_ _Assertion_ _Expected_ _|_ _optional message__**
95+
96+
_**~a: demo.multiply(1,2) equal 2 | should multiply numbers**_
97+
98+
#### Supported Assertions
99+
100+
Assertion | Function
101+
---------|-------
102+
equal | asserts equality
103+
notEqual | asserts inequality
104+
deepEqual | asserts deep equality - use when comparing objects
105+
notDeepEqual | asserts deep inequality - use when comparing objects

SAMPLE-webpack.config.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
const path = require('path')
2+
const webpack = require('webpack');
3+
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
4+
const tapered = require('tapered.js/tapered-webpack-plugin.js');
5+
6+
module.exports = {
7+
entry: ['./src/index.js', './index.js'],
8+
output: {
9+
path: path.join(__dirname, '/client'),
10+
filename: 'bundle.js'
11+
},
12+
module: {
13+
rules: [
14+
{ test: /jsx?/,
15+
exclude: /node_modules/,
16+
use: 'babel-loader',
17+
},
18+
{
19+
test: /scss$/,
20+
exclude: /node_modules/,
21+
use: ['style-loader', 'css-loader', 'sass-loader']
22+
}
23+
]
24+
},
25+
plugins : [
26+
new tapered(),
27+
new webpack.optimize.UglifyJsPlugin({
28+
extractComments: {
29+
condition: /ßdNß0j1/,
30+
filename: 'INSERT TEST FILE LOCATION HERE'
31+
},
32+
}),
33+
],
34+
}

SAMPLE.babelrc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"presets": ["env", "react"],
3+
"plugins": ["tapered.js/tapered-babel-plugin.js"]
4+
}

package.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"name": "tapered.js",
3+
"version": "0.6.3",
4+
"description": "Easy inline test-writing using Tape",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1"
8+
},
9+
"author": "badNboji",
10+
"license": "MIT",
11+
"dependencies": {
12+
"babel-cli": "^6.24.1",
13+
"babel-core": "^6.25.0",
14+
"babel-loader": "^7.1.0",
15+
"babel-preset-env": "^1.5.2",
16+
"tape": "^4.6.3",
17+
"webpack": "^3.0.0"
18+
}
19+
}

tapered-babel-plugin.js

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
module.exports = function({types: t}) {
2+
let tapeCount = 0;
3+
return {
4+
visitor: {
5+
Program(path, state) {
6+
// console.log("filename" ,state.file.opts);
7+
// if there are comments in the program file
8+
if (path.parent.comments.length) {
9+
const comments = path.parent.comments
10+
// loop through the comments
11+
for (let i = 0; i < comments.length; i += 1) {
12+
// console.log('comments', path.parent.comments[i].value)
13+
// require tape library setup
14+
// const reqTape = " dabTape */"
15+
// require file name setup
16+
const reqFileName = " ß∂dNß0j1const " + state.file.opts.sourceMapTarget.slice(0, state.file.opts.sourceMapTarget.length - 3) + " = require('" + state.file.opts.filename + "');";
17+
// if a comments requires Tape then replace with complete require statement
18+
// TODO FIX TAPE SYNTAX!!!!!!!!!
19+
if (comments[i].value.includes('%tapered')) {
20+
// if (tapeCount === 0) {
21+
comments[i].value = reqFileName;
22+
// comments[i].value = reqTape + "\n" + "/*" + reqFileName;
23+
// tapeCount++;
24+
// } else {
25+
// comments[i].value = reqFileName;
26+
// tapeCount++;
27+
// }
28+
}
29+
// ----------------------------------------------------------------------------
30+
// SECTION INCLUDES NAME/DESCRIPTION - ASSERTION AND VARIABLES
31+
// Split on squigglys/tildas
32+
if (comments[i].value.includes('~')) {
33+
let currcomments = comments[i].value.split("~");
34+
// currcomments[0] is empty
35+
// currcomments[1] = name/description and is required
36+
let test = ' ß∂dNß0j1test';
37+
let description = currcomments[1].replace(/\r\n/, "\n").split(/\n/)[0].replace(/^[ ]+|[ ]+$/g, '');
38+
if (description[0] === 'x' && description[1] === ':') {
39+
description = description.slice(2).replace(/^[ ]+|[ ]+$/g, '');
40+
test = ' ß∂dNß0j1test.skip';
41+
};
42+
if (description[0] === 'o' && description[1] === ':') {
43+
description = description.slice(2).replace(/^[ ]+|[ ]+$/g, '');
44+
test = ' ß∂dNß0j1test.only';
45+
};
46+
let actual;
47+
let expression;
48+
let expected;
49+
let errMessage;
50+
let resultOfAssertion = "";
51+
let assertion;
52+
let startIndExpression;
53+
let expressionEndPoint;
54+
let argumentLength = 0;
55+
let variables;
56+
let finalCommentsTranspiled = "";
57+
let endTest = "\t" + "t.end();" + "\n";
58+
// Helper function that splits the ASSERTION: actual, expected, and expression
59+
function assertions(string) {
60+
let argumentSplit = string.split("|");
61+
let threeArgExpression = [
62+
"equal",
63+
"notEqual",
64+
"deepEqual",
65+
"notDeepEqual",
66+
"deepLooseEqual",
67+
"notDeepLooseEqual",
68+
"throws",
69+
"doesNotThrow",
70+
"same",
71+
"notSame",
72+
"strictSame",
73+
"strictNotSame"
74+
];
75+
let twoArgExpression = ["ok", "notOk", "error"];
76+
// Find the expression start index and end index
77+
for (let three = 0; three < threeArgExpression.length; three++) {
78+
if (argumentSplit[0].indexOf(threeArgExpression[three]) !== -1) {
79+
startIndExpression = argumentSplit[0].indexOf(threeArgExpression[three]);
80+
expressionEndPoint = startIndExpression + threeArgExpression[three].length;
81+
argumentLength = 3;
82+
}
83+
}
84+
for (let two = 0; two < twoArgExpression.length; two++) {
85+
if (argumentSplit[0].indexOf(twoArgExpression[two]) !== -1) {
86+
startIndExpression = argumentSplit[0].indexOf(twoArgExpression[two]);
87+
expressionEndPoint = startIndExpression + twoArgExpression[two].length;
88+
argumentLength = 2;
89+
}
90+
}
91+
// If assertion contains an error message
92+
if (argumentLength === 3) {
93+
if (argumentSplit.length > 1) {
94+
// console.log("1st", argumentSplit);
95+
actual = argumentSplit[0].slice(0, startIndExpression).replace(/^[ ]+|[ ]+$/g, '');
96+
expression = argumentSplit[0].slice(startIndExpression, expressionEndPoint).replace(/^[ ]+|[ ]+$/g, '');
97+
expected = argumentSplit[0].slice(expressionEndPoint).replace(/\r\n/, "\n").split(/\n/)[0];
98+
let message = argumentSplit[1].replace(/\s*[\r\n]+\s*/g, "\n").split(/\n/)[0].replace(/^[ ]+|[ ]+$/g, '');
99+
errMessage = "'" + message + "'";
100+
}
101+
// If assertion does not contain an error message
102+
if (argumentSplit.length < 2) {
103+
// console.log("2nd", argumentSplit[0]);
104+
actual = argumentSplit[0].slice(0, startIndExpression).replace(/^[ ]+|[ ]+$/g, '');
105+
expression = argumentSplit[0].slice(startIndExpression, expressionEndPoint).replace(/^[ ]+|[ ]+$/g, '');
106+
expected = argumentSplit[0].slice(expressionEndPoint).replace(/\r\n/, "\n").split(/\n/)[0];
107+
errMessage = "'" + "Error: " + description + "'";
108+
}
109+
resultOfAssertion = "\t" + "t." + expression + "(" + actual + ", " + expected + ", " + errMessage + ");" + "\n";
110+
return resultOfAssertion;
111+
}
112+
if (argumentLength === 2) {
113+
if (argumentSplit.length > 1) {
114+
// console.log("1st", argumentSplit);
115+
expression = argumentSplit[0].slice(startIndExpression, expressionEndPoint).replace(/^[ ]+|[ ]+$/g, '');
116+
expected = argumentSplit[0].slice(expressionEndPoint).replace(/\r\n/, "\n").split(/\n/)[0];
117+
let message = argumentSplit[1].replace(/\s*[\r\n]+\s*/g, "\n").split(/\n/)[0].replace(/^[ ]+|[ ]+$/g, '');
118+
errMessage = "'" + message + "'";
119+
}
120+
// If assertion does not contain an error message
121+
if (argumentSplit.length < 2) {
122+
// console.log("2nd", argumentSplit[0]);
123+
expression = argumentSplit[0].slice(startIndExpression, expressionEndPoint).replace(/^[ ]+|[ ]+$/g, '');
124+
expected = argumentSplit[0].slice(expressionEndPoint).replace(/\r\n/, "\n").split(/\n/)[0];
125+
errMessage = "'" + "Error: " + description + "'";
126+
}
127+
resultOfAssertion = "\t" + "t." + expression + "(" + expected + ", " + errMessage + ");" + "\n";
128+
return resultOfAssertion;
129+
}
130+
}
131+
for (let index = 2; index < currcomments.length; index++) {
132+
// if comments[index] is an assertion
133+
if (currcomments[index].indexOf("a:") !== -1 && currcomments[index][0] === 'a' && currcomments[index][1] === ':') {
134+
// console.log("this is assertion", currcomments[index].slice(2).replace(/^[ ]+|[ ]+$/g, ''));
135+
assertion = assertions(currcomments[index].slice(2).replace(/^[ ]+|[ ]+$/g, ''));
136+
finalCommentsTranspiled += assertion;
137+
}
138+
// if currcomments[index] is a variable (optional)
139+
if (currcomments[index][0] !== 'a' && currcomments[index][1] !== ':' && currcomments[index] !== undefined && /\S/.test(currcomments[index])) {
140+
variables = "\t" + currcomments[index].replace(/^[ ]+|[ ]+$/g, '');
141+
finalCommentsTranspiled += variables;
142+
}
143+
//if curcomments[index] is ending in plan
144+
if (currcomments[index].indexOf("p:") !== -1 && currcomments[index][0] === 'p' && currcomments[index][1] === ':') {
145+
// let plan = "\t" + currcomments[index].slice(2).replace(/^[ ]+|[ ]+$/g, '');
146+
let planInput = currcomments[index].slice(2).replace(/\s/g, '');
147+
endTest = '';
148+
finalCommentsTranspiled += `\tt.plan(${planInput});\n`
149+
}
150+
}
151+
comments[i].value = test + "('" + description + "', function (t) {" + "\n" + finalCommentsTranspiled + endTest + "});" ;
152+
}
153+
}
154+
}
155+
}
156+
}
157+
};
158+
}

tapered-webpack-plugin.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
2+
function webpackCommentExtractionPlugin() {}
3+
4+
webpackCommentExtractionPlugin.prototype.apply = function(compiler) {
5+
compiler.plugin('emit', function(compilation, callback) {
6+
function unComment() {
7+
const file = compilation.assets['../test/tape-test-sample.js'];
8+
if (file === undefined) {
9+
console.log('file is undefined')
10+
// Refactor with Promises?
11+
setTimeout(function(){unComment}, 1000);
12+
} else if (file) {
13+
console.log('file is defined')
14+
// if (file.source().includes('dabTape')) {
15+
compilation.assets['../test/tape-test-sample.js'] = {
16+
source: function() {
17+
console.log(file.source());
18+
// remove BadNBojiTape if we move away from multiple file/framework feature
19+
return "const test = require('tape')" + "\n" + file.source().replace(/(\/\*\ ßdNß0j1Tape)|(\/\*\ ßdNß0j1)|(\*\/)/g,'');
20+
},
21+
size: function() {
22+
return file.source().length;
23+
}
24+
};
25+
// }
26+
}
27+
}
28+
callback();
29+
unComment();
30+
});
31+
};
32+
33+
module.exports = webpackCommentExtractionPlugin;

0 commit comments

Comments
 (0)