Skip to content

Commit

Permalink
v1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
stevenvachon committed Jan 4, 2019
1 parent bd70f8b commit 104ddb8
Show file tree
Hide file tree
Showing 7 changed files with 326 additions and 62 deletions.
63 changes: 3 additions & 60 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,61 +1,4 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
coverage/
node_modules/
jspm_packages/

# TypeScript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env

# next.js build output
.next
.nyc_output/
package-lock.json
6 changes: 6 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
language: node_js
node_js:
- 8
- 10
- node
script: npm run ci
45 changes: 43 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,43 @@
# incomplete-symbol
Custom-remove features of a Symbol implementation.
# incomplete-symbol [![NPM Version][npm-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Coverage Status][coveralls-image]][coveralls-url]

> Custom-remove features of a [`Symbol`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Symbol) implementation.

This is useful when simulating the incomplete `Symbol` implementations available in some of today's modern web browsers.


## Installation

[Node.js](http://nodejs.org/) `>= 8` is required. To install, type this at the command line:
```shell
npm install incomplete-symbol
```


## Usage

```js
const customizeSymbol = require('incomplete-symbol');

const exclusions = ['toStringTag'];

const IncompleteSymbol = customizeSymbol(exclusions);

const symbol = new IncompleteSymbol('foo');
```


## Arguments

### `exclusions`
Type: `Array`
Default value: `[]`
The output `Symbol` function will not expose each listed property/method.


[npm-image]: https://img.shields.io/npm/v/incomplete-symbol.svg
[npm-url]: https://npmjs.com/package/incomplete-symbol
[travis-image]: https://img.shields.io/travis/stevenvachon/incomplete-symbol.svg
[travis-url]: https://travis-ci.org/stevenvachon/incomplete-symbol
[coveralls-image]: https://img.shields.io/coveralls/stevenvachon/incomplete-symbol.svg
[coveralls-url]: https://coveralls.io/github/stevenvachon/incomplete-symbol
46 changes: 46 additions & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"use strict";
const {instanceMethods, instanceProperties, staticMethods, staticProperties} = require("./props");



const customizeSymbol = (exclusions=[]) =>
{
// For testing: uses the global value when this function is called
// instead of when instances are created
const globalSymbol = Symbol;

function IncompleteSymbolInstance(description)
{
this._symbol = globalSymbol(description);

instanceMethods
.filter(method => !exclusions.includes(method) && typeof this._symbol[method]==="function")
.forEach(method => this[method] = (...args) => this._symbol[method](...args));

instanceProperties
.filter(property => !exclusions.includes(property) && this._symbol[property]!==undefined)
.forEach(property => Object.defineProperty(this, property,
{
get: () => this._symbol[property]
}));
}

const IncompleteSymbol = (...args) => new IncompleteSymbolInstance(...args);

staticMethods
.filter(method => !exclusions.includes(method) && typeof globalSymbol[method]==="function")
.forEach(method => IncompleteSymbol[method] = (...args) => globalSymbol[method](...args));

staticProperties
.filter(property => !exclusions.includes(property) && property in globalSymbol)
.forEach(property => Object.defineProperty(IncompleteSymbol, property,
{
get: () => globalSymbol[property]
}));

return IncompleteSymbol;
};



module.exports = customizeSymbol;
54 changes: 54 additions & 0 deletions lib/props.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"use strict";



const instanceMethods =
[
"toString",
"valueOf",
Symbol.toPrimitive
];



const instanceProperties =
[
"constructor",
"description"
];



const staticMethods =
[
"for",
"keyFor"
];



const staticProperties =
[
"hasInstance",
"isConcatSpreadable",
"iterator",
"length",
"match",
"replace",
"search",
"species",
"split",
"toPrimitive",
"toStringTag",
"unscopables"
];



module.exports =
{
instanceMethods,
instanceProperties,
staticMethods,
staticProperties
};
32 changes: 32 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "incomplete-symbol",
"description": "Custom-remove features of a Symbol implementation.",
"version": "1.0.0",
"license": "MIT",
"author": "Steven Vachon <contact@svachon.com> (https://www.svachon.com/)",
"repository": "github:stevenvachon/incomplete-symbol",
"main": "lib",
"devDependencies": {
"chai": "^4.2.0",
"coveralls": "^3.0.2",
"mocha": "^5.2.0",
"nyc": "^13.1.0",
"sinon": "^7.2.2",
"sinon-chai": "^3.3.0",
"symbol.prototype.description": "^1.0.0"
},
"engines": {
"node": ">= 8"
},
"scripts": {
"ci": "npm run test && nyc report --reporter=text-lcov | coveralls",
"posttest": "nyc report --reporter=html",
"test": "nyc --reporter=text-summary mocha test --check-leaks --bail"
},
"files": [
"lib"
],
"keywords": [
"symbol"
]
}
142 changes: 142 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
"use strict";
const customizeSymbol = require("./lib");
const {afterEach, beforeEach, describe, it} = require("mocha");
const {expect} = require("chai").use( require("sinon-chai") );
const {instanceMethods, instanceProperties, staticMethods, staticProperties} = require("./lib/props");
const {restore, stub} = require("sinon");
require("symbol.prototype.description/auto");



const instanceKeys = [...instanceMethods, ...instanceProperties];

const propertyString = key => typeof key === "symbol" ? key.description : `"${key}"`;

const removeExtendableKeys = keys => keys.filter(key => unextendableKeys.includes(key));
const removeUnextendableKeys = keys => keys.filter(key => !unextendableKeys.includes(key));

const staticKeys = [...staticMethods, ...staticProperties];
const symbolString = "description";

const unextendableKeys =
[
"constructor",
"length",
"toString",
"valueOf"
];



describe("Default behavior", () =>
{
it("is the same as the global", () =>
{
const IncompleteSymbol = customizeSymbol();

const symbol = IncompleteSymbol(symbolString);

instanceKeys.forEach(key => expect(symbol).to.have.property(key));
staticKeys.forEach(key => expect(IncompleteSymbol).to.have.property(key));

const args = ["arg1", "arg2"];

instanceMethods.forEach(method =>
{
stub(Symbol.prototype, method);

symbol[method](...args);

expect(Symbol.prototype[method]).to.have.been.calledWith(...args);

restore();
});

staticMethods.forEach(method =>
{
stub(Symbol, method);

IncompleteSymbol[method](...args);

expect(Symbol[method]).to.have.been.calledWith(...args);

restore();
});
});
});



describe("Unavailable properties/methods", () =>
{
const originalSymbol = Symbol;



beforeEach(() =>
{
const stubbedSymbol = customizeSymbol(
[
"description",
"for",
"hasInstance",
Symbol.toPrimitive
]);

stub(global, "Symbol").callsFake(stubbedSymbol);
});

afterEach(() => restore());



it("does not attempt to wrap them", () =>
{
const IncompleteSymbol = customizeSymbol();

const symbol = IncompleteSymbol(symbolString);

expect(IncompleteSymbol).to.not.have.property("for");
expect(IncompleteSymbol).to.not.have.property("hasInstance");
expect(symbol).to.not.have.property("description");
expect(symbol).to.not.have.property(originalSymbol.toPrimitive);
})
});



describe("Exclusions", () =>
{
removeUnextendableKeys(instanceKeys).forEach(key => it(propertyString(key), () =>
{
const IncompleteSymbol = customizeSymbol([key]);

const symbol = IncompleteSymbol(symbolString);

expect(symbol).to.not.have.property(key);
}));



removeUnextendableKeys(staticKeys).forEach(key => it(propertyString(key), () =>
{
const IncompleteSymbol = customizeSymbol([key]);

expect(IncompleteSymbol).to.not.have.property(key);
}));



it("all methods/properties", () =>
{
const IncompleteSymbol = customizeSymbol([...instanceKeys, ...staticKeys]);

const symbol = IncompleteSymbol(symbolString);

removeExtendableKeys(instanceKeys).forEach(key => expect(symbol).to.have.property(key));
removeExtendableKeys(staticKeys).forEach(key => expect(IncompleteSymbol).to.have.property(key));

removeUnextendableKeys(instanceKeys).forEach(key => expect(symbol).to.not.have.property(key));
removeUnextendableKeys(staticKeys).forEach(key => expect(IncompleteSymbol).to.not.have.property(key));
});
});

0 comments on commit 104ddb8

Please sign in to comment.