diff --git a/README.md b/README.md index 231e83b..d8029b2 100755 --- a/README.md +++ b/README.md @@ -83,6 +83,7 @@ Sections help divide up the page into logical groups of items. It is recommended |----------|------|-------------| | type | string | Set to `section`. | | items | array | Array of items to include in this section. | +| capabilities | array | Array of features that the connected watch must have for this section to be present | ##### Example @@ -125,6 +126,7 @@ Headings can be used in anywhere and can have their size adjusted to suit the co | appKey | string (unique) | The AppMessage key matching the `appKey` item defined in your `appinfo.json`. Set this to a unique string to allow this item to be looked up using `Clay.getItemsByAppKey()` in your custom function. You must set this if you wish for the value of this item to be persisted after the user closes the config page. | | defaultValue | string/HTML | The heading's text. | | size | int | Defaults to `4`. An integer from 1 to 6 where 1 is the largest size and 6 is the smallest. (represents HTML `

`, `

`, `

`, etc). | +| capabilities | array | Array of features that the connected watch must have for this item to be present | ##### Example @@ -154,6 +156,7 @@ Text is used to provide descriptions of sections or to explain complex parts of | id | string (unique) | Set this to a unique string to allow this item to be looked up using `Clay.getItemsById()` in your [custom function](#custom-function). | | appKey | string (unique) | The AppMessage key matching the `appKey` item defined in your `appinfo.json`. Set this to a unique string to allow this item to be looked up using `Clay.getItemsByAppKey()` in your custom function. You must set this if you wish for the value of this item to be persisted after the user closes the config page. | | defaultValue | string/HTML | The content of the text element. | +| capabilities | array | Array of features that the connected watch must have for this item to be present | ##### Example @@ -184,6 +187,7 @@ Standard text input field. | defaultValue | string | The default value of the input field. | | description | string | Optional sub-text to include below the component | | attributes | object | An object containing HTML attributes to set on the input field. You can add basic HTML5 validation this way by setting attributes such as `required` or `type`. | +| capabilities | array | Array of features that the connected watch must have for this item to be present | ##### Example @@ -222,6 +226,7 @@ Switch for a single item. | defaultValue | int\|boolean | The default value of the toggle. Defaults to `false` if not specified. | | description | string | Optional sub-text to include below the component | | attributes | object | An object containing HTML attributes to set on the input field. You can add basic HTML5 validation this way by setting attribute such as `required`. | +| capabilities | array | Array of features that the connected watch must have for this item to be present | ##### Example @@ -258,6 +263,7 @@ A dropdown menu containing multiple options. | description | string | Optional sub-text to include below the component | | attributes | object | An object containing HTML attributes to set on the input field. You can add basic HTML5 validation this way by setting attribute such as `required`. | | options | array of objects | The options you want to appear in the dropdown menu. Each option is an object with a `label` and `value` property. | +| capabilities | array | Array of features that the connected watch must have for this item to be present | ##### Example @@ -371,6 +377,7 @@ The color picker will automatically show a different layout depending on the wat | sunlight | boolean | Use the color-corrected sunlight color palette if `true`, else the uncorrected version. Defaults to `true` if not specified. | | layout | string OR array | Optional. Use a custom layout for the color picker. Defaults to automatically choosing the most appropriate layout for the connected watch. The layout is represented by a two dimensional array. Use `false` to insert blank spaces. You may also use one of the preset layouts by setting `layout` to: `"COLOR"`, `"GRAY"` or `"BLACK_WHITE"` | | allowGray | boolean | Optional. Set this to `true` to include gray (`#AAAAAA`) in the color picker for aplite running on firmware 3 and above. This is optional because only a subset of the drawing operations support gray on aplite. Defaults to `false` | +| capabilities | array | Array of features that the connected watch must have for this item to be present | ##### Example @@ -435,6 +442,7 @@ A list of options allowing the user can only choose one option to submit. | description | string | Optional sub-text to include below the component | | attributes | object | An object containing HTML attributes to set on the input field. You can add basic HTML5 validation this way by setting attribute such as `required`. | | options | array of objects | The options you want to appear in the radio group. Each option is an object with a `label` and `value` property. | +| capabilities | array | Array of features that the connected watch must have for this item to be present | ##### Example @@ -480,6 +488,7 @@ A list of options where a user may choose more than one option to submit. | description | string | Optional sub-text to include below the component | | attributes | object | An object containing HTML attributes to set on the input field. You can add basic HTML5 validation this way by setting attribute such as `required`. | | options | array of objects | The options you want to appear in the checkbox group. Each option is an object with a `label` and `value` property. | +| capabilities | array | Array of features that the connected watch must have for this item to be present | ##### Example @@ -521,6 +530,7 @@ A list of options where a user may choose more than one option to submit. | primary | boolean | If `true` the button will be orange, if `false`, the button will be gray (defaults to `false`)| | attributes | object | An object containing HTML attributes to set on the input field. | | description | string | Optional sub-text to include below the component | +| capabilities | array | Array of features that the connected watch must have for this item to be present | ##### Example @@ -559,6 +569,7 @@ you must just remember to divide the received value on the watch accordingly. | step | number | The multiple of the values allowed to be set on the slider. The slider will snap to these values. This value also determines the precision used when the value is sent to the watch. Defaults to 1 | | attributes | object | An object containing HTML attributes to set on the input field. | | description | string | Optional sub-text to include below the component | +| capabilities | array | Array of features that the connected watch must have for this item to be present | ##### Example @@ -590,6 +601,7 @@ The submit button for the page. You **MUST** include this component somewhere in | type | string | Set to `submit`. | | defaultValue | string | The text displayed on the button. | | attributes | object | An object containing HTML attributes to set on the input field. | +| capabilities | array | Array of features that the connected watch must have for this item to be present | ##### Example @@ -609,6 +621,64 @@ The submit button for the page. You **MUST** include this component somewhere in - Dynamic + draggable list --- +### Showing items for specific platforms and features + +If you want particular items or sections to only be present in the config based on the +connected watch's capabilities, you can include the "capabilities" property in the item. +The "capabilities" property is an array of features that the connected watch must have +in order for the item or section to be included in the page. Note: all capabilities in +the array must be present for item/section to be included. + +**Warning:** Items that do not satisfy the capabilities will not be included in the page +at all. You will not be able to use methods like `clayConfig.getItemByAppKey()` to +obttain a reference to them. However, this does mean that you will be able to have +multiple items with the same `appKey`as long as they do not both satisfy the +same conditions. + + +##### Examples + +```javascript +{ + "type": "text", + "defaultValue": "This item will only be visible for watches that are rectangular and have a microphone" + "capabilities": ["MICROPHONE", "RECT"], +} +``` + +```javascript +{ + "type": "section", + "capabilities": ["COLOR"], + "items": [ + { + "type": "text", + "defaultValue": "Only visible for color watches" + } + ] +} +``` + +Below is the full list of capabilities + +| Capability | Description | +|------------|-------------| +| APLITE | Running on Pebble/Pebble Steel.| +| BASALT | Running on Pebble Time/Pebble Time Steel. | +| CHALK | Running on Pebble Time Round. | +| DIORITE | Running on Pebble 2 | +| EMERY | Running on Time 2. | +| BW | Running on hardware that supports only black and white. | +| COLOR | Running on hardware that supports 64 colors. | +| MICROPHONE | Running on hardware that includes a microphone. | +| SMARTSTRAP | Running on hardware that includes a smartstrap connector. | +| SMARTSTRAP_POWER | Running on hardware that includes a powered smartstrap connector. | +| HEALTH | Running on hardware that supports Pebble Health and the HealthService API. | +| RECT | Running on hardware with a rectangular display. | +| ROUND | Running on hardware with a round display. | +| DISPLAY_144x168 | Running on hardware with a 144x168 pixels display. | +| DISPLAY_180x180_ROUND | Running on hardware with a 180x180 pixels round display. | +| DISPLAY_200x228 | Running on hardware with a 200x228 pixels display. | ## Manipulators diff --git a/dev/config.js b/dev/config.js index 8067e4d..96e3033 100644 --- a/dev/config.js +++ b/dev/config.js @@ -13,6 +13,17 @@ module.exports = [ "defaultValue": "Some arbitrary text explaining how this all works. " + "It's cool if this wraps across multiple lines" }, + { + "type": "section", + "capabilities": ["RECT", "MICROPHONE"], + "items": [ + { + "type": "text", + "defaultValue": "This section is only visible for rectangular devices that" + + " have a microphone" + } + ] + }, { "type": "section", "items": [ @@ -20,6 +31,11 @@ module.exports = [ "type": "heading", "defaultValue": "This is a section" }, + { + "type": "text", + "defaultValue": "This is only visible for aplite", + "capabilities": ["APLITE"] + }, { "type": "input", "appKey": "email", diff --git a/dev/dev.js b/dev/dev.js index 502edd2..8e5d518 100644 --- a/dev/dev.js +++ b/dev/dev.js @@ -6,6 +6,17 @@ window.claySettings = {}; window.customFn = require('./custom-fn.js'); window.clayComponents = require('../src/scripts/components'); window.clayMeta = require('../test/fixture').meta({ + activeWatchInfo: { + platform: 'basalt', + model: 'qemu_platform_basalt', + language: 'en_US', + firmware: { + major: 3, + minor: 10, + patch: 2, + suffix: '' + } + }, userData: { foo: 'bar', config2: require('./config-2') diff --git a/src/scripts/lib/clay-config.js b/src/scripts/lib/clay-config.js index 285a064..99a17e3 100644 --- a/src/scripts/lib/clay-config.js +++ b/src/scripts/lib/clay-config.js @@ -11,6 +11,7 @@ * @property {Object} [attributes] * @property {Array} [options] * @property {Array} [items] + * @property {Array} [capabilities] */ var HTML = require('../vendor/minified').HTML; @@ -61,34 +62,36 @@ function ClayConfig(settings, config, $rootContainer, meta) { item.forEach(function(item) { _addItems(item, $container); }); - } else if (item.type === 'section') { - var $wrapper = HTML('
'); - $container.add($wrapper); - _addItems(item.items, $wrapper); - } else { - var _item = _.copyObj(item); - _item.clayId = _items.length; + } else if (utils.includesCapability(meta.activeWatchInfo, item.capabilities)) { + if (item.type === 'section') { + var $wrapper = HTML('
'); + $container.add($wrapper); + _addItems(item.items, $wrapper); + } else { + var _item = _.copyObj(item); + _item.clayId = _items.length; - var clayItem = new ClayItem(_item).initialize(self); + var clayItem = new ClayItem(_item).initialize(self); - if (_item.id) { - _itemsById[_item.id] = clayItem; + if (_item.id) { + _itemsById[_item.id] = clayItem; + } + + if (_item.appKey) { + _itemsByAppKey[_item.appKey] = clayItem; + } + + _items.push(clayItem); + + // set the value of the item via the manipulator to ensure consistency + var value = typeof _settings[_item.appKey] !== 'undefined' ? + _settings[_item.appKey] : + (_item.defaultValue || ''); + + clayItem.set(value); + + $container.add(clayItem.$element); } - - if (_item.appKey) { - _itemsByAppKey[_item.appKey] = clayItem; - } - - _items.push(clayItem); - - // set the value of the item via the manipulator to ensure consistency - var value = typeof _settings[_item.appKey] !== 'undefined' ? - _settings[_item.appKey] : - (_item.defaultValue || ''); - - clayItem.set(value); - - $container.add(clayItem.$element); } } diff --git a/src/scripts/lib/utils.js b/src/scripts/lib/utils.js index 02079f3..2e2e54a 100644 --- a/src/scripts/lib/utils.js +++ b/src/scripts/lib/utils.js @@ -17,3 +17,112 @@ module.exports.updateProperties = function(obj, descriptor) { Object.defineProperty(obj, prop, descriptor); }); }; + +module.exports.capabilityMap = { + APLITE: { + platforms: ['aplite'], + minFwMajor: 0, + minFwMinor: 0 + }, + BASALT: { + platforms: ['basalt'], + minFwMajor: 0, + minFwMinor: 0 + }, + CHALK: { + platforms: ['chalk'], + minFwMajor: 0, + minFwMinor: 0 + }, + DIORITE: { + platforms: ['diorite'], + minFwMajor: 0, + minFwMinor: 0 + }, + EMERY: { + platforms: ['emery'], + minFwMajor: 0, + minFwMinor: 0 + }, + BW: { + platforms: ['aplite', 'diorite'], + minFwMajor: 0, + minFwMinor: 0 + }, + COLOR: { + platforms: ['basalt', 'chalk', 'emery'], + minFwMajor: 0, + minFwMinor: 0 + }, + MICROPHONE: { + platforms: ['basalt', 'chalk', 'diorite', 'emery'], + minFwMajor: 0, + minFwMinor: 0 + }, + SMARTSTRAP: { + platforms: ['basalt', 'chalk', 'diorite', 'emery'], + minFwMajor: 3, + minFwMinor: 4 + }, + SMARTSTRAP_POWER: { + platforms: ['basalt', 'chalk', 'emery'], + minFwMajor: 3, + minFwMinor: 4 + }, + HEALTH: { + platforms: ['basalt', 'chalk', 'diorite', 'emery'], + minFwMajor: 3, + minFwMinor: 10 + }, + RECT: { + platforms: ['aplite', 'basalt', 'diorite', 'emery'], + minFwMajor: 0, + minFwMinor: 0 + }, + ROUND: { + platforms: ['chalk'], + minFwMajor: 3, + minFwMinor: 4 + }, + DISPLAY_144x168: { + platforms: ['aplite', 'basalt', 'diorite'], + minFwMajor: 0, + minFwMinor: 0 + }, + DISPLAY_180x180_ROUND: { + platforms: ['chalk'], + minFwMajor: 0, + minFwMinor: 0 + }, + DISPLAY_200x228: { + platforms: ['emery'], + minFwMajor: 0, + minFwMinor: 0 + } +}; + +/** + * Checks if all of the provided capabilities are compatible with the watch + * @param {Object} activeWatchInfo + * @param {Array} [capabilities] + * @return {boolean} + */ +module.exports.includesCapability = function(activeWatchInfo, capabilities) { + if (!capabilities || !capabilities.length) { + return true; + } + + for (var i = capabilities.length - 1; i >= 0; i--) { + var capability = capabilities[i]; + var mapping = module.exports.capabilityMap[capability]; + if (!mapping || + mapping.platforms.indexOf(activeWatchInfo.platform) === -1 || + mapping.minFwMajor > activeWatchInfo.firmware.major || + mapping.minFwMajor === activeWatchInfo.firmware.major && + mapping.minFwMinor > activeWatchInfo.firmware.minor + ) { + return false; + } + } + return true; +}; diff --git a/test/spec/lib/clay-config.js b/test/spec/lib/clay-config.js index a977935..eab242f 100644 --- a/test/spec/lib/clay-config.js +++ b/test/spec/lib/clay-config.js @@ -27,6 +27,177 @@ describe('ClayConfig', function() { checkReadOnly(clayConfig, properties); }); + /** + * Tests if the provided capabilities of the platform match + * @param {string} platform + * @param {number} fwMajor + * @param {number} fwMinor + * @param {Array} capabilities + * @param {number} expected + * @return {void} + */ + function testCapabilities(platform, fwMajor, fwMinor, capabilities, expected) { + it(platform + ' is' + (expected ? '' : ' not') + ' included on firmware ' + + fwMajor + '.' + fwMinor + ' for capabilities ' + JSON.stringify(capabilities), + function() { + var item = { + type: 'input', + capabilities: capabilities + }; + var meta = { + activeWatchInfo: { + platform: platform, + model: 'qemu_platform_' + platform, + language: 'en_US', + firmware: { + major: fwMajor, + minor: fwMinor, + patch: 0, + suffix: '' + } + } + }; + var clayConfig = fixtures.clayConfig([item], true, true, null, meta); + + assert.strictEqual(clayConfig.getAllItems().length, expected); + }); + } + + describe('capability filtering', function() { + testCapabilities('aplite', 2, 9, ['BW'], 1); + testCapabilities('aplite', 2, 9, ['COLOR'], 0); + testCapabilities('aplite', 2, 9, ['MICROPHONE'], 0); + testCapabilities('aplite', 2, 9, ['SMARTSTRAP'], 0); + testCapabilities('aplite', 2, 9, ['SMARTSTRAP_POWER'], 0); + testCapabilities('aplite', 2, 9, ['HEALTH'], 0); + testCapabilities('aplite', 2, 9, ['RECT'], 1); + testCapabilities('aplite', 2, 9, ['ROUND'], 0); + testCapabilities('aplite', 2, 9, ['DISPLAY_144x168'], 1); + testCapabilities('aplite', 2, 9, ['DISPLAY_180x180_ROUND'], 0); + testCapabilities('aplite', 2, 9, ['DISPLAY_200x228'], 0); + testCapabilities('aplite', 2, 9, ['APLITE'], 1); + testCapabilities('aplite', 2, 9, ['BASALT'], 0); + testCapabilities('aplite', 2, 9, ['CHALK'], 0); + testCapabilities('aplite', 2, 9, ['DIORITE'], 0); + testCapabilities('aplite', 2, 9, ['EMERY'], 0); + + testCapabilities('aplite', 3, 10, ['BW'], 1); + testCapabilities('aplite', 3, 10, ['COLOR'], 0); + testCapabilities('aplite', 3, 10, ['MICROPHONE'], 0); + testCapabilities('aplite', 3, 10, ['SMARTSTRAP'], 0); + testCapabilities('aplite', 3, 4, ['SMARTSTRAP'], 0); + testCapabilities('aplite', 3, 3, ['SMARTSTRAP'], 0); + testCapabilities('aplite', 3, 10, ['SMARTSTRAP_POWER'], 0); + testCapabilities('aplite', 3, 10, ['HEALTH'], 0); + testCapabilities('aplite', 3, 9, ['HEALTH'], 0); + testCapabilities('aplite', 3, 10, ['RECT'], 1); + testCapabilities('aplite', 3, 10, ['ROUND'], 0); + testCapabilities('aplite', 3, 10, ['DISPLAY_144x168'], 1); + testCapabilities('aplite', 3, 10, ['DISPLAY_180x180_ROUND'], 0); + testCapabilities('aplite', 3, 10, ['DISPLAY_200x228'], 0); + testCapabilities('aplite', 3, 10, ['APLITE'], 1); + testCapabilities('aplite', 3, 10, ['BASALT'], 0); + testCapabilities('aplite', 3, 10, ['CHALK'], 0); + testCapabilities('aplite', 3, 10, ['DIORITE'], 0); + testCapabilities('aplite', 3, 10, ['EMERY'], 0); + + testCapabilities('basalt', 3, 10, ['BW'], 0); + testCapabilities('basalt', 3, 10, ['COLOR'], 1); + testCapabilities('basalt', 3, 10, ['MICROPHONE'], 1); + testCapabilities('basalt', 3, 10, ['SMARTSTRAP'], 1); + testCapabilities('basalt', 3, 4, ['SMARTSTRAP'], 1); + testCapabilities('basalt', 3, 3, ['SMARTSTRAP'], 0); + testCapabilities('basalt', 3, 10, ['SMARTSTRAP_POWER'], 1); + testCapabilities('basalt', 3, 4, ['SMARTSTRAP_POWER'], 1); + testCapabilities('basalt', 3, 3, ['SMARTSTRAP_POWER'], 0); + testCapabilities('basalt', 3, 10, ['HEALTH'], 1); + testCapabilities('basalt', 3, 9, ['HEALTH'], 0); + testCapabilities('basalt', 3, 10, ['RECT'], 1); + testCapabilities('basalt', 3, 10, ['ROUND'], 0); + testCapabilities('basalt', 3, 10, ['DISPLAY_144x168'], 1); + testCapabilities('basalt', 3, 10, ['DISPLAY_180x180_ROUND'], 0); + testCapabilities('basalt', 3, 10, ['DISPLAY_200x228'], 0); + testCapabilities('basalt', 3, 10, ['APLITE'], 0); + testCapabilities('basalt', 3, 10, ['BASALT'], 1); + testCapabilities('basalt', 3, 10, ['CHALK'], 0); + testCapabilities('basalt', 3, 10, ['DIORITE'], 0); + testCapabilities('basalt', 3, 10, ['EMERY'], 0); + + testCapabilities('chalk', 3, 10, ['BW'], 0); + testCapabilities('chalk', 3, 10, ['COLOR'], 1); + testCapabilities('chalk', 3, 10, ['MICROPHONE'], 1); + testCapabilities('chalk', 3, 10, ['SMARTSTRAP'], 1); + testCapabilities('chalk', 3, 4, ['SMARTSTRAP'], 1); + testCapabilities('chalk', 3, 3, ['SMARTSTRAP'], 0); + testCapabilities('chalk', 3, 10, ['SMARTSTRAP_POWER'], 1); + testCapabilities('chalk', 3, 4, ['SMARTSTRAP_POWER'], 1); + testCapabilities('chalk', 3, 3, ['SMARTSTRAP_POWER'], 0); + testCapabilities('chalk', 3, 10, ['HEALTH'], 1); + testCapabilities('chalk', 3, 9, ['HEALTH'], 0); + testCapabilities('chalk', 3, 10, ['RECT'], 0); + testCapabilities('chalk', 3, 10, ['ROUND'], 1); + testCapabilities('chalk', 3, 10, ['DISPLAY_144x168'], 0); + testCapabilities('chalk', 3, 10, ['DISPLAY_180x180_ROUND'], 1); + testCapabilities('chalk', 3, 10, ['DISPLAY_200x228'], 0); + testCapabilities('chalk', 3, 10, ['APLITE'], 0); + testCapabilities('chalk', 3, 10, ['BASALT'], 0); + testCapabilities('chalk', 3, 10, ['CHALK'], 1); + testCapabilities('chalk', 3, 10, ['DIORITE'], 0); + testCapabilities('chalk', 3, 10, ['EMERY'], 0); + + testCapabilities('diorite', 3, 10, ['BW'], 1); + testCapabilities('diorite', 3, 10, ['COLOR'], 0); + testCapabilities('diorite', 3, 10, ['MICROPHONE'], 1); + testCapabilities('diorite', 3, 10, ['SMARTSTRAP'], 1); + testCapabilities('diorite', 3, 4, ['SMARTSTRAP'], 1); + testCapabilities('diorite', 3, 3, ['SMARTSTRAP'], 0); + testCapabilities('diorite', 3, 10, ['SMARTSTRAP_POWER'], 0); + testCapabilities('diorite', 3, 4, ['SMARTSTRAP_POWER'], 0); + testCapabilities('diorite', 3, 3, ['SMARTSTRAP_POWER'], 0); + testCapabilities('diorite', 3, 10, ['HEALTH'], 1); + testCapabilities('diorite', 3, 9, ['HEALTH'], 0); + testCapabilities('diorite', 3, 10, ['RECT'], 1); + testCapabilities('diorite', 3, 10, ['ROUND'], 0); + testCapabilities('diorite', 3, 10, ['DISPLAY_144x168'], 1); + testCapabilities('diorite', 3, 10, ['DISPLAY_180x180_ROUND'], 0); + testCapabilities('diorite', 3, 10, ['DISPLAY_200x228'], 0); + testCapabilities('diorite', 3, 10, ['APLITE'], 0); + testCapabilities('diorite', 3, 10, ['BASALT'], 0); + testCapabilities('diorite', 3, 10, ['CHALK'], 0); + testCapabilities('diorite', 3, 10, ['DIORITE'], 1); + testCapabilities('diorite', 3, 10, ['EMERY'], 0); + + testCapabilities('emery', 3, 10, ['BW'], 0); + testCapabilities('emery', 3, 10, ['COLOR'], 1); + testCapabilities('emery', 3, 10, ['MICROPHONE'], 1); + testCapabilities('emery', 3, 10, ['SMARTSTRAP'], 1); + testCapabilities('emery', 3, 4, ['SMARTSTRAP'], 1); + testCapabilities('emery', 3, 3, ['SMARTSTRAP'], 0); + testCapabilities('emery', 3, 10, ['SMARTSTRAP_POWER'], 1); + testCapabilities('emery', 3, 4, ['SMARTSTRAP_POWER'], 1); + testCapabilities('emery', 3, 3, ['SMARTSTRAP_POWER'], 0); + testCapabilities('emery', 3, 10, ['HEALTH'], 1); + testCapabilities('emery', 3, 9, ['HEALTH'], 0); + testCapabilities('emery', 3, 10, ['RECT'], 1); + testCapabilities('emery', 3, 10, ['ROUND'], 0); + testCapabilities('emery', 3, 10, ['DISPLAY_144x168'], 0); + testCapabilities('emery', 3, 10, ['DISPLAY_180x180_ROUND'], 0); + testCapabilities('emery', 3, 10, ['DISPLAY_200x228'], 1); + testCapabilities('emery', 3, 10, ['APLITE'], 0); + testCapabilities('emery', 3, 10, ['BASALT'], 0); + testCapabilities('emery', 3, 10, ['CHALK'], 0); + testCapabilities('emery', 3, 10, ['DIORITE'], 0); + testCapabilities('emery', 3, 10, ['EMERY'], 1); + + testCapabilities('aplite', 3, 10, ['BW', 'HEALTH'], 0); + testCapabilities('emery', 3, 10, ['COLOR', 'HEALTH'], 1); + testCapabilities('emery', 3, 10, ['MICROPHONE'], 1); + testCapabilities('chalk', 3, 10, ['APLITE', 'BASALT', 'DIORITE'], 0); + testCapabilities('basalt', 3, 10, ['SMARTSTRAP', 'SMARTSTRAP_POWER'], 1); + testCapabilities('aplite', 3, 10, ['COLOR', 'APLITE'], 0); + testCapabilities('aplite', 3, 10, ['APLITE', 'COLOR'], 0); + }); + describe('.meta', function() { it('populates meta', function() { var clayConfig = fixtures.clayConfig(['input']);