From a8fe0b6129d409c48dd30ad0477d54f12ca3af48 Mon Sep 17 00:00:00 2001 From: Lyza Danger Gardner Date: Tue, 12 Apr 2016 11:39:07 -0700 Subject: [PATCH 1/4] Add ability to use nested templates as default layout for pages --- src/render/collections.js | 7 +++---- test/render/pages.js | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/render/collections.js b/src/render/collections.js index 14ff831..840129c 100644 --- a/src/render/collections.js +++ b/src/render/collections.js @@ -1,5 +1,6 @@ import { resourceContext } from '../utils/context'; import { applyTemplate } from '../utils/render'; +import { deepObj } from '../utils/object'; /** * For any given `patterns` entry, render a pattern-collection page for @@ -14,12 +15,10 @@ import { applyTemplate } from '../utils/render'; * @return {Object} patterns data at this level. */ function renderCollection (patterns, drizzleData, collectionKey) { - // TODO right now, layoutKey will only work if it is top-level in - // templates directory. Also, there is no override supported on a collection- - // by-collection level (i.e. in collection metadata files). const layoutKey = drizzleData.options.layouts.collection; + const layoutObj = deepObj(layoutKey.split('.'), drizzleData.templates, false); patterns.collection.contents = applyTemplate( - drizzleData.templates[layoutKey].contents, + layoutObj.contents, resourceContext(patterns.collection, drizzleData), drizzleData.options); return patterns; diff --git a/test/render/pages.js b/test/render/pages.js index af41dae..9f14d3e 100644 --- a/test/render/pages.js +++ b/test/render/pages.js @@ -18,6 +18,7 @@ describe ('render/pages', () => { expect(pageData['04-sandbox'].contents) .to.have.string('

Sandbox

'); }); + it ('should throw if layout is missing'); it ('should be able to deal with empty pages', () => { // Technically we know it can because we got this far and there is an // empty page file in the fixtures From 14f29bf76821394c7a7c1298829173c5f2c5d549 Mon Sep 17 00:00:00 2001 From: Lyza Danger Gardner Date: Tue, 12 Apr 2016 13:30:53 -0700 Subject: [PATCH 2/4] Replace extremely problematic deepmerge module with deep-extend --- package.json | 6 ++- src/init.js | 7 ++-- src/render/collections.js | 14 ++++++- test/parse/data.js | 9 ++-- test/parse/templates.js | 2 +- test/render/collections.js | 86 +++++++++++++++++++++++--------------- test/render/index.js | 6 +-- 7 files changed, 83 insertions(+), 47 deletions(-) diff --git a/package.json b/package.json index a7f4ed7..4961a71 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,9 @@ "version": "0.0.1", "description": "The builder for Drizzle", "main": "dist/index.js", - "files": ["dist"], + "files": [ + "dist" + ], "engines": { "node": ">=4.0.0" }, @@ -21,7 +23,7 @@ "repository": "cloudfour/drizzle-builder", "dependencies": { "bluebird": "^3.3.1", - "deepmerge": "^0.2.10", + "deep-extend": "^0.4.1", "front-matter": "^2.0.6", "globby": "^4.0.0", "handlebars": "^4.0.5", diff --git a/src/init.js b/src/init.js index 0c6df7d..3b3e40c 100644 --- a/src/init.js +++ b/src/init.js @@ -1,4 +1,4 @@ -import merge from 'deepmerge'; +import deepExtend from 'deep-extend'; import defaults from './defaults'; import Promise from 'bluebird'; @@ -10,8 +10,9 @@ import Promise from 'bluebird'; * @return {Promise} resolving to merged options */ function init (options = {}, handlebars) { - options.handlebars = handlebars || require('handlebars'); - return Promise.resolve(merge(defaults, options)); + const opts = deepExtend({}, defaults, options); + opts.handlebars = handlebars || require('handlebars'); + return Promise.resolve(opts); } export default init; diff --git a/src/render/collections.js b/src/render/collections.js index 840129c..ae6ef59 100644 --- a/src/render/collections.js +++ b/src/render/collections.js @@ -1,6 +1,7 @@ import { resourceContext } from '../utils/context'; import { applyTemplate } from '../utils/render'; import { deepObj } from '../utils/object'; +import DrizzleError from '../utils/error'; /** * For any given `patterns` entry, render a pattern-collection page for @@ -16,7 +17,18 @@ import { deepObj } from '../utils/object'; */ function renderCollection (patterns, drizzleData, collectionKey) { const layoutKey = drizzleData.options.layouts.collection; - const layoutObj = deepObj(layoutKey.split('.'), drizzleData.templates, false); + let layoutObj; + try { + // deepObj will throw if it fails, which is good and fine... + layoutObj = deepObj(layoutKey.split('.'), drizzleData.templates, false); + } catch (e) { + // But Make this error more friendly and specific + DrizzleError.error(new DrizzleError( + `Could not find partial for default collection layout +'${layoutKey}'. Check 'options.layouts.collection' and/or +'options.src.templates' values to make sure they are OK`, + DrizzleError.LEVELS.ERROR), drizzleData.options.debug); + } patterns.collection.contents = applyTemplate( layoutObj.contents, resourceContext(patterns.collection, drizzleData), diff --git a/test/parse/data.js b/test/parse/data.js index 78b3bb2..d307bae 100644 --- a/test/parse/data.js +++ b/test/parse/data.js @@ -4,10 +4,13 @@ var expect = chai.expect; var parseData = require('../../dist/parse/data'); describe ('parse/data', () => { - var opts = config.init(config.fixtureOpts); - var fileKeys = ['another-data', 'data-as-json', 'sample-data']; + var opts; + before (() => { + return config.init(config.fixtureOpts).then(options => opts = options); + }); it ('should parse data from files', () => { - return opts.then(parseData).then(allData => { + var fileKeys = ['another-data', 'data-as-json', 'sample-data']; + parseData(opts).then(allData => { expect(allData).to.be.an('object').and.to.contain.keys(fileKeys); }); }); diff --git a/test/parse/templates.js b/test/parse/templates.js index ef36ff6..f9a1f67 100644 --- a/test/parse/templates.js +++ b/test/parse/templates.js @@ -4,8 +4,8 @@ var expect = chai.expect; var parseTemplates = require('../../dist/parse/templates'); describe ('parse/templates', () => { - var opts = config.init(config.fixtureOpts); it ('should parse and organize templates', () => { + var opts = config.init(config.fixtureOpts); return opts.then(parseTemplates).then(templateData => { expect(templateData).to.contain.keys( 'collection', 'default', 'page', 'partials'); diff --git a/test/render/collections.js b/test/render/collections.js index dbaa8f6..ada056e 100644 --- a/test/render/collections.js +++ b/test/render/collections.js @@ -5,44 +5,62 @@ var expect = chai.expect; var prepare = require('../../dist/prepare/'); var parse = require('../../dist/parse/'); var renderPatterns = require('../../dist/render/collections'); +var defaults = require('../../dist/defaults'); describe ('render/collections', () => { - var opts, patternData; - before (() => { - opts = config.init(config.fixtureOpts); - return opts.then(prepare).then(parse).then(renderPatterns).then(pData => { - patternData = pData; + describe ('generating render output and data', () => { + var opts, patternData; + before (() => { + opts = config.init(config.fixtureOpts); + return opts.then(prepare).then(parse).then(renderPatterns).then(pData => { + patternData = pData; + }); + }); + it ('should return patterns data', () => { + expect(patternData).to.be.an('object'); + expect(patternData).to.contain.keys('collection', 'components'); + }); + it ('should provide metadata for pattern collections', () => { + expect(patternData).to.contain.keys('collection'); + expect(patternData['fingers']).to.contain.keys('collection'); + expect(patternData.components.collection).to.contain.keys('contents'); + // `typography` doesn't have any immediate-child pattern files + expect(patternData.typography).not.to.contain.key('collection'); + }); + it ('should add a name property for collections', () => { + expect(patternData).not.to.contain.keys('contents'); + expect(patternData.collection.name).to.equal('Patterns'); + expect(patternData['fingers'].collection).to.contain.keys( + 'name', 'contents'); + expect(patternData['fingers'].collection.name).to.equal('Fingers'); + expect(patternData.components.button.collection.name) + .to.equal('Not a Button'); + }); + it ('should render pattern collections with proper context', () => { + expect(patternData.typography).to.be.an('object'); + expect(patternData.typography.headings).to.be.an('object'); + expect(patternData.typography.headings) + .to.contain.keys('collection'); + expect(patternData.collection.contents).to.contain('

Patterns

'); + expect(patternData.components.button.collection.contents).to.contain( + 'class="f-Item-control"' + ); }); }); - it ('should return patterns data', () => { - expect(patternData).to.be.an('object'); - expect(patternData).to.contain.keys('collection', 'components'); - }); - it ('should provide metadata for pattern collections', () => { - expect(patternData).to.contain.keys('collection'); - expect(patternData['fingers']).to.contain.keys('collection'); - expect(patternData.components.collection).to.contain.keys('contents'); - // `typography` doesn't have any immediate-child pattern files - expect(patternData.typography).not.to.contain.key('collection'); - }); - it ('should add a name property for collections', () => { - expect(patternData).not.to.contain.keys('contents'); - expect(patternData.collection.name).to.equal('Patterns'); - expect(patternData['fingers'].collection).to.contain.keys( - 'name', 'contents'); - expect(patternData['fingers'].collection.name).to.equal('Fingers'); - expect(patternData.components.button.collection.name) - .to.equal('Not a Button'); - }); - it ('should render pattern collections with proper context', () => { - expect(patternData.typography).to.be.an('object'); - expect(patternData.typography.headings).to.be.an('object'); - expect(patternData.typography.headings) - .to.contain.keys('collection'); - expect(patternData.collection.contents).to.contain('

Patterns

'); - expect(patternData.components.button.collection.contents).to.contain( - 'class="f-Item-control"' - ); + describe ('error states', () => { + var opts; + before (() => { + return config.init(config.fixtureOpts).then(options => opts = options); + }); + it ('should throw if default layout is missing', () => { + opts.layouts.collection = 'i.do.not.exist'; + return prepare(opts).then(parse).then(renderPatterns).catch(error => { + expect(error.message).to.contain('not find partial for default'); + console.log(opts.layouts === defaults.layouts); + }); + + }); }); + }); diff --git a/test/render/index.js b/test/render/index.js index 867c78f..be8da49 100644 --- a/test/render/index.js +++ b/test/render/index.js @@ -6,10 +6,10 @@ var parse = require('../../dist/parse/'); var renderAll = require('../../dist/render/'); describe ('render/index (renderAll)', () => { - var opts, drizzleData; + var drizzleData; before (() => { - opts = config.init(config.fixtureOpts); - return opts.then(prepare).then(parse).then(renderAll).then(dData => { + return config.init(config.fixtureOpts).then(prepare) + .then(parse).then(renderAll).then(dData => { drizzleData = dData; }); }); From ab50b81a7cf8dc0768ff1ccd9208cbbee43d7566 Mon Sep 17 00:00:00 2001 From: Lyza Danger Gardner Date: Tue, 12 Apr 2016 14:41:34 -0700 Subject: [PATCH 3/4] Get a start on some documentation about resources --- README.md | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/README.md b/README.md index 37ba3a1..266e9bb 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,96 @@ drizzle(options).then(drizzleData => { }); ``` +## Authoring with Drizzle + +### Creating Resources + +Drizzle builds static HTML output from source resource files. + +* _Pages_ are compiled against a page layout template and output as HTML files. +* _Patterns_ are parsed into collections, which are compiled against a collection layout template and output as HTML files. +* _Data_ files can be used to provide data to patterns and pages. + +#### Front Matter, YAML and JSON + +Pages and patterns can use YAML front matter to override defaults, set particular attributes or provide arbitrary local data for the resource during template compilation. In general, data defined in front matter is made available at the top level of compilation (template) context for that resource. + +##### Reserved Properties + +Certain properties are reserved at the top level for Drizzle use. Each resource has its own reserved keys as well, but the following keys should not be used in front matter for any resource: + +* `contents`: Used by Drizzle to store parsed and rendered contents for resources. +* `data`: Used for storing pattern- and page-specific data in global objects. +* `drizzle`: The `drizzle` property is reserved for global context during template compilation. +* `outputPath`: Used by Drizzle to record where the resource should be output to the file system. +* `path`: Used by Drizzle to retain the path to the original resource source file. + +#### Formats and Parsing + +#### Keys and Object Structure + +#### Pages + +Each file that matches the `pages` glob (`options.src.pages.glob`) will generate an HTML page. + +##### Special Properties + +* `layout`: Specify the layout template to use for this page resource (default layout templated is defined in `options.layouts.page`). + +#### Patterns + +##### Special Properties + +* `name`: Override default naming for the pattern, which is based on filename. +* `hidden`: A truthy value will "hide" this pattern, making it available as data, but not rendered on its collection's page. +* `order`: Numeric value for where this pattern should appear in its collection's list of patterns. Defaults to alphabetical by filename. + +##### Reserved Properties + +* `collection`: Used by Drizzle to attach some metadata about this pattern's collection. + +#### Collections + +Collections are "meta" resources. Each directory within the glob match for `options.src.patterns.glob` that contains at least one matching pattern file is considered a "collection." By default, collections are named based on their directory name. One output HTML page is generated per collection. + +##### Collection Metadata + +Creating a file named `collection.yaml`, `collection.yml` or `collection.json` in a pattern directory allows you to override data about that collection. Accepted properties are: + +* `name` `{String}`: Override default directory-based naming +* `hidden` : An `Array` of `{String}` pattern ids to hide in the collection's output (base filename without extension) +* `order`: An `Array` of `{String}` pattern ids in the order you'd like them to display + +`hidden` and `order` values can also be defined in individual patterns' front matter. Local pattern data will override data in `collection` metadata files. + +#### Data + +Files that match `options.src.data.globs` will be parsed and made available to templates. See documentation about global scope in the templates section. + +### Templates + +#### Context + +The context available to resources during template compilation is a combination of a shared global context and a local context specific to the resource. + +##### Global Context + +Templates receive a `drizzle` data at the top level of their context. Top-level keys on this object are: + +* `data`: Structured object of parsed data files +* `options`: Object of options that drizzle is using +* `pages`: Structured object of all page data +* `patterns`: Structured object of all patterns +* `templates`: Structured object of all templates (partials, layouts) and their contents + +##### Local Context + + +#### handlebars-layouts + +#### Default Templates + + ## API ### drizzle([options]) From 6a4f3018a14c97723f379d2ea34a24a48dde5cad Mon Sep 17 00:00:00 2001 From: Lyza Danger Gardner Date: Tue, 12 Apr 2016 15:33:30 -0700 Subject: [PATCH 4/4] Add namespacing checking on patterns and collections --- README.md | 9 +- src/parse/patterns.js | 23 ++- .../badPatterns/anotherBadCollection/bar.html | 0 .../anotherBadCollection/collection.yml | 5 + .../badPatterns/badCollection/collection.yml | 4 + .../badPatterns/badCollection/foo.html | 0 test/fixtures/badPatterns/localId.html | 6 + test/parse/patterns.js | 136 ++++++++++++------ 8 files changed, 137 insertions(+), 46 deletions(-) create mode 100644 test/fixtures/badPatterns/anotherBadCollection/bar.html create mode 100644 test/fixtures/badPatterns/anotherBadCollection/collection.yml create mode 100644 test/fixtures/badPatterns/badCollection/collection.yml create mode 100644 test/fixtures/badPatterns/badCollection/foo.html create mode 100644 test/fixtures/badPatterns/localId.html diff --git a/README.md b/README.md index 266e9bb..dc03ed7 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ Each file that matches the `pages` glob (`options.src.pages.glob`) will generate Collections are "meta" resources. Each directory within the glob match for `options.src.patterns.glob` that contains at least one matching pattern file is considered a "collection." By default, collections are named based on their directory name. One output HTML page is generated per collection. -##### Collection Metadata +##### Collection Metadata (Special Properties) Creating a file named `collection.yaml`, `collection.yml` or `collection.json` in a pattern directory allows you to override data about that collection. Accepted properties are: @@ -80,6 +80,13 @@ Creating a file named `collection.yaml`, `collection.yml` or `collection.json` i `hidden` and `order` values can also be defined in individual patterns' front matter. Local pattern data will override data in `collection` metadata files. +Unlike other resources, properties in `collection` metadata files that are _not_ one the properties listed here will be ignored. + +##### Reserved Properties + +* `items`: Used by Drizzle to store _all_ of the patterns in this collection (even hidden ones) +* `patterns`: Used by Drizzle to store all of the _visible_, _ordered_ patterns in this collection. + #### Data Files that match `options.src.data.globs` will be parsed and made available to templates. See documentation about global scope in the templates section. diff --git a/src/parse/patterns.js b/src/parse/patterns.js index 060ddbe..82eafdd 100644 --- a/src/parse/patterns.js +++ b/src/parse/patterns.js @@ -12,6 +12,19 @@ const isPattern = obj => obj.hasOwnProperty('path'); const collectionPath = itms => path.dirname(itms[Object.keys(itms).pop()].path); const collectionKey = itms => collectionPath(itms).split(path.sep).pop(); +function checkNamespaceCollision (key, obj, id, options = {}) { + if (Array.isArray(key)) { + return key.map(oneKey => + checkNamespaceCollision(oneKey, obj, id, options) + ); + } + if (obj && obj.hasOwnProperty(key)) { + DrizzleError.error(new DrizzleError(`'${id}' has local '${key}' +property set. This is a Drizzle reserved property and will be +overwritten. See docs.`, DrizzleError.LEVELS.WARN), options.debug); + } +} + /** * Should this pattern be hidden per collection or pattern metadata? * @param {Object} collection Collection obj @@ -65,8 +78,12 @@ const hasPatternOrdering = itms => { */ function buildPattern (patternObj, options) { const patternFile = { path: patternObj.path }; + const patternId = resourceId(patternFile, + options.src.patterns.basedir, 'patterns'); + checkNamespaceCollision('id', patternObj.data, + `Pattern ${patternId}`, options); return Object.assign(patternObj, { - id: resourceId(patternFile, options.src.patterns.basedir, 'patterns'), + id: patternId, name: (patternObj.data && patternObj.data.name) || titleCase(resourceKey(patternFile)) }); @@ -148,9 +165,11 @@ function buildCollection (collectionObj, options) { return readFiles(collectionGlob(items), options).then(collData => { const collectionMeta = (collData.length) ? collData[0].contents : {}; collectionObj.collection = Object.assign ({ - items: items, name: titleCase(collectionKey(items)) }, collectionMeta); + checkNamespaceCollision(['items', 'patterns'], collectionObj.collection, + `Collection ${collectionObj.collection.name}`, options); + collectionObj.collection.items = items; collectionObj.collection.patterns = buildOrderedPatterns( collectionObj.collection); return collectionObj; diff --git a/test/fixtures/badPatterns/anotherBadCollection/bar.html b/test/fixtures/badPatterns/anotherBadCollection/bar.html new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/badPatterns/anotherBadCollection/collection.yml b/test/fixtures/badPatterns/anotherBadCollection/collection.yml new file mode 100644 index 0000000..ac5bd8c --- /dev/null +++ b/test/fixtures/badPatterns/anotherBadCollection/collection.yml @@ -0,0 +1,5 @@ +patterns: + - this + - is + - not + - OK diff --git a/test/fixtures/badPatterns/badCollection/collection.yml b/test/fixtures/badPatterns/badCollection/collection.yml new file mode 100644 index 0000000..d391aae --- /dev/null +++ b/test/fixtures/badPatterns/badCollection/collection.yml @@ -0,0 +1,4 @@ +items: + - no + - no nope + - not here diff --git a/test/fixtures/badPatterns/badCollection/foo.html b/test/fixtures/badPatterns/badCollection/foo.html new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/badPatterns/localId.html b/test/fixtures/badPatterns/localId.html new file mode 100644 index 0000000..43c1966 --- /dev/null +++ b/test/fixtures/badPatterns/localId.html @@ -0,0 +1,6 @@ +--- +id: "This is a no-no" +name: "Overridden Name Is Fine" +--- + +This pattern tries to define a local ID. diff --git a/test/parse/patterns.js b/test/parse/patterns.js index 3044e6c..362ac8b 100644 --- a/test/parse/patterns.js +++ b/test/parse/patterns.js @@ -3,36 +3,44 @@ var config = require('../config'); var expect = chai.expect; var parsePatterns = require('../../dist/parse/patterns'); var utils = require('../../dist/utils/object'); +var DrizzleError = require('../../dist/utils/error'); describe ('parse/patterns', () => { - var opts, patternData; - before (() => { - opts = config.init(config.fixtureOpts); - return opts.then(parsePatterns).then(pData => { - patternData = pData; + describe('building pattern data', () => { + var opts, patternData; + before (() => { + opts = config.init(config.fixtureOpts); + return opts.then(parsePatterns).then(pData => { + patternData = pData; + }); }); - }); - it ('should correctly build data object from patterns', () => { - expect(patternData).to.be.an('object'); - expect(patternData).to.have.keys( - 'collection', 'fingers', 'components', 'typography'); - expect(patternData.collection.items).to.have.keys('pink'); - }); - describe('basic collection data on patterns', () => { - it ('should add basic collection data', () => { - expect(patternData.collection).to.contain.keys( - 'name', 'items', 'patterns'); + it ('should correctly build data object from patterns', () => { + expect(patternData).to.be.an('object'); + expect(patternData).to.have.keys( + 'collection', 'fingers', 'components', 'typography'); + expect(patternData.collection.items).to.have.keys('pink'); + }); + describe('basic collection data on patterns', () => { + it ('should add basic collection data', () => { + expect(patternData.collection).to.contain.keys( + 'name', 'items', 'patterns'); + }); }); }); - describe('`name` prop override', () => { + describe ('overriding data with properties', () => { + var opts, patternData; + before (() => { + opts = config.init(config.fixtureOpts); + return opts.then(parsePatterns).then(pData => { + patternData = pData; + }); + }); it ('should allow override of name property', () => { expect(patternData.components.button.collection.items.aardvark) .to.contain.key('name'); expect(patternData.components.button.collection.items.aardvark.name) .to.equal('Something Else'); }); - }); - describe ('data field parsing', () => { it ('should run data fields through parsers', () => { var ideal = patternData.fingers.collection.items.ideal; expect(ideal).to.contain.keys('data'); @@ -40,16 +48,12 @@ describe ('parse/patterns', () => { expect(ideal.data.ancillary).to.be.a('string'); expect(ideal.data.ancillary).to.contain('
    '); }); - }); - describe ('pattern object structure', () => { it ('should define the appropriate properties for each pattern', () => { var aPattern = patternData.components.button.collection.items.base; expect(aPattern).to.have.keys('id', 'name', 'data', 'path', 'contents'); expect(aPattern).not.to.have.keys('notes', 'links'); expect(aPattern.data).to.contain.keys('notes', 'links'); }); - }); - describe('pattern IDs', () => { it ('should derive IDs for patterns', () => { expect(patternData.collection.items.pink).to.include.key('id'); expect(patternData.collection.items.pink.id).to.equal('patterns.pink'); @@ -64,35 +68,81 @@ describe ('parse/patterns', () => { expect(utils.deepPattern(longId, patternData)).to.be.an('object') .and.to.contain.keys('name', 'data', 'path', 'id', 'contents'); }); + it ('should add relevant properties to individual pattern objects', () => { + expect(patternData.collection.items.pink).to.be.an('object'); + expect(patternData.collection.items.pink).to.have.keys( + 'name', 'id', 'contents', 'path', 'data'); + }); }); - it ('should add relevant properties to individual pattern objects', () => { - expect(patternData.collection.items.pink).to.be.an('object'); - expect(patternData.collection.items.pink).to.have.keys( - 'name', 'id', 'contents', 'path', 'data'); - }); - describe ('parse/collections', () => { + describe('parsing collections', () => { + var opts, patternData; + before (() => { + opts = config.init(config.fixtureOpts); + return opts.then(parsePatterns).then(pData => { + patternData = pData; + }); + }); it ('should create basic stub objects for collections', () => { var collection = patternData.components.button.collection; expect(collection.name).not.to.be; expect(collection).to.contain.keys('items', 'patterns'); }); - describe ('hidden patterns', () => { - it ('should hide patterns that have front matter to that effect', () => { - var collection = patternData.components.button.collection; - expect(collection.patterns).not.to.contain(collection.items.hello); - expect(collection.patterns).to.contain(collection.items.base); + it ('should hide patterns that have front matter to that effect', () => { + var collection = patternData.components.button.collection; + expect(collection.patterns).not.to.contain(collection.items.hello); + expect(collection.patterns).to.contain(collection.items.base); + }); + it ('should order patterns per front matter', () => { + var collection = patternData.components.button.collection; + expect(collection.patterns[0]).to.equal(collection.items.disabled); + expect(collection.patterns[1]).to + .equal(collection.items['color-variation']); + expect(collection.patterns[2]).to.equal(collection.items.aardvark); + expect(collection.patterns[3]).to.equal(collection.items.base); + }); + }); + describe ('pattern error situations', () => { + var opts; + beforeEach (() => { + return config.init(config.fixtureOpts).then(options => opts = options); + }); + it ('should raise an error if pattern has `id` set locally', () => { + opts.src.patterns = { + glob: config.fixturePath('badPatterns/localId.html'), + basedir: config.fixturePath('badPatterns') + }; + return parsePatterns(opts).catch(error => { + expect(error).to.be.instanceof(DrizzleError); + expect(error.message).to.contain('Drizzle reserved property'); + }); + }); + }); + describe ('collection error situations', () => { + var opts; + beforeEach (() => { + return config.init(config.fixtureOpts).then(options => opts = options); + }); + it ('should raise an error if collection has `items` set locally', () => { + opts.src.patterns = { + glob: config.fixturePath('badPatterns/badCollection/*.html'), + basedir: config.fixturePath('badPatterns') + }; + return parsePatterns(opts).catch(error => { + expect(error).to.be.instanceof(DrizzleError); + expect(error.message).to.contain('Drizzle reserved property'); + expect(error.message).to.contain('items'); }); }); - describe ('pattern ordering', () => { - it ('should order patterns per front matter', () => { - var collection = patternData.components.button.collection; - expect(collection.patterns[0]).to.equal(collection.items.disabled); - expect(collection.patterns[1]).to - .equal(collection.items['color-variation']); - expect(collection.patterns[2]).to.equal(collection.items.aardvark); - expect(collection.patterns[3]).to.equal(collection.items.base); + it ('should raise an error if collection has `patterns` set', () => { + opts.src.patterns = { + glob: config.fixturePath('badPatterns/anotherBadCollection/*.html'), + basedir: config.fixturePath('badPatterns') + }; + return parsePatterns(opts).catch(error => { + expect(error).to.be.instanceof(DrizzleError); + expect(error.message).to.contain('Drizzle reserved property'); + expect(error.message).to.contain('patterns'); }); }); }); - });