diff --git a/.eslintignore b/.eslintignore index c74edf369c8..2003cec41a0 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,5 +1,5 @@ blueprints/*/*files/**/*.js node-tests/fixtures/**/*.js -docs/ +/docs/ dist/ tmp/ diff --git a/.eslintrc.js b/.eslintrc.js index 1bcdf4d9518..9ba8cc9a672 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -111,6 +111,7 @@ module.exports = { 'tests/node/**/*.js', 'blueprints/**/*.js', 'bin/**/*.js', + 'tests/docs/*.js', 'config/**/*.js', 'lib/**/*.js', 'server/**/*.js', diff --git a/.gitignore b/.gitignore index b66c9f446b8..470f0c4920f 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,7 @@ assets/bpm_libs.js assets/bpm_styles.css coverage dist -docs +/docs lib/*/tests/all.js lib/*/tests/qunit* lib/bundler/man diff --git a/bin/run-tests.js b/bin/run-tests.js index 5efcd082a0b..abeb6ad3208 100755 --- a/bin/run-tests.js +++ b/bin/run-tests.js @@ -140,6 +140,7 @@ function codeQualityChecks() { runChecker('node', [require.resolve('typescript/bin/tsc'), '--noEmit']), runChecker('node', [require.resolve('tslint/bin/tslint'), '-p', 'tsconfig.json']), runChecker('node', [require.resolve('eslint/bin/eslint'), '.']), + runChecker('node', [require.resolve('qunit/bin/qunit'), 'tests/docs/coverage-test.js']), ]; return Promise.all(checkers).then(function(results) { results.forEach(result => { diff --git a/tests/docs/coverage-test.js b/tests/docs/coverage-test.js new file mode 100644 index 00000000000..31c76fe2da6 --- /dev/null +++ b/tests/docs/coverage-test.js @@ -0,0 +1,65 @@ +/* eslint-disable no-console */ +'use strict'; + +const QUnit = require('qunit'); +const test = QUnit.test; +const path = require('path'); + +QUnit.module('Docs coverage', function(hooks) { + let docs, expected; + hooks.before(function() { + if (!process.env.REUSE_DOCS) { + buildDocs(); + } + docs = require(path.join(__dirname, '../../docs/data.json')); + expected = require('./expected'); + }); + + QUnit.module('classitems', function(hooks) { + let docsItems, expectedItems; + hooks.before(function() { + docsItems = new Set(docs.classitems.map(item => item.name).filter(Boolean)); + expectedItems = new Set(expected.classitems); + }); + + test('No missing classitems', function(assert) { + let missing = setDifference(expectedItems, docsItems); + assert.emptySet( + missing, + 'If you have added new features, please update tests/docs/expected.js and confirm that any public properties are marked both @public and @static to be included in the Ember API Docs viewer.' + ); + }); + + test('No extraneous classitems', function(assert) { + let extraneous = setDifference(docsItems, expectedItems); + assert.emptySet( + extraneous, + 'If you intentionally removed a public API method, please udpate tests/docs/expected.js. Otherwise, documentation is missing, incorrectly formatted, or in a directory that is not watched by yuidoc. All files containing documentation must have a yuidoc class declaration.' + ); + }); + }); +}); + +function buildDocs() { + let child = require('child_process'); + child.execFileSync('node', [require.resolve('ember-cli/bin/ember'), 'ember-cli-yuidoc'], { + stdio: 'pipe', + }); +} + +function setDifference(setA, setB) { + let difference = new Set(setA); + for (var elem of setB) { + difference.delete(elem); + } + return difference; +} + +QUnit.assert.emptySet = function assertEmptySet(value, message) { + this.pushResult({ + result: value.size === 0, + actual: Array.from(value).sort(), + expected: [], + message: message, + }); +}; diff --git a/tests/docs/expected.js b/tests/docs/expected.js new file mode 100644 index 00000000000..ad715322a30 --- /dev/null +++ b/tests/docs/expected.js @@ -0,0 +1,589 @@ +module.exports = { + classitems: [ + '$', + '@each', + 'A', + 'EXTEND_PROTOTYPES', + 'GUID_KEY', + 'GUID_PREFIX', + 'LOG_STACKTRACE_ON_DEPRECATION', + 'LOG_VERSION', + '[]', + '_APPLICATION_TEMPLATE_WRAPPER', + '_JQUERY_INTEGRATION', + '_TEMPLATE_ONLY_GLIMMER_COMPONENTS', + '__container__', + '_activeQPChanged', + '_applicationInstances', + '_deserializeQueryParam', + '_deserializeQueryParams', + '_fullyScopeQueryParams', + '_getHashPath', + '_getObjectsOnNamespaces', + '_getQPMeta', + '_globalsMode', + '_helpers', + '_hydrateUnsuppliedQueryParams', + '_initializersRan', + '_injections', + '_internalReset', + '_invoke', + '_lazyInjections', + '_logLookup', + '_names', + '_normalizeCache', + '_onLookup', + '_options', + '_optionsForQueryParam', + '_prepareForGlobalsMode', + '_prepareQueryParams', + '_pruneDefaultQueryParamValues', + '_qp', + '_qpChanged', + '_qpDelegate', + '_queryParamsFor', + '_renderMode', + '_resolveCache', + '_scheduledDestroy', + '_serializeQueryParam', + '_serializeQueryParams', + '_setRouteName', + '_stashNames', + '_typeInjections', + '_typeOptions', + '_unwatchInstance', + '_updatingQPChanged', + '_watchInstance', + 'abort', + 'acceptsModelName', + 'action', + 'actions', + 'activate', + 'active', + 'activeClass', + 'adapter', + 'add', + 'addArrayObserver', + 'addListener', + 'addObject', + 'addObjects', + 'addObserver', + 'advanceReadiness', + 'afterModel', + 'alias', + 'aliasMethod', + 'all', + 'allSettled', + 'and', + 'any', + 'append', + 'appendTo', + 'application', + 'apply', + 'ariaRole', + 'arrangedContent', + 'array', + 'arrayContentDidChange', + 'arrayContentWillChange', + 'assert', + 'assign', + 'asyncEnd', + 'asyncStart', + 'attributeBindings', + 'attributeLimit', + 'attributes', + 'autoboot', + 'beforeModel', + 'begin', + 'beginPropertyChanges', + 'bind', + 'bool', + 'boot', + 'bubbles', + 'buildChildEngineInstance', + 'buildInstance', + 'buildRegistry', + 'cache', + 'cacheFor', + 'camelize', + 'canCatalogEntriesByType', + 'canInvoke', + 'cancel', + 'cancelRouterSetup', + 'capitalize', + 'catalogEntriesByType', + 'catch', + 'changeProperties', + 'checkWaiters', + 'childViews', + 'classNameBindings', + 'classNames', + 'classify', + 'clear', + 'click', + 'cloneParentDependencies', + 'collect', + 'columnsForType', + 'compact', + 'compare', + 'component', + 'compute', + 'computed', + 'concat', + 'concatenatedProperties', + 'container', + 'containerDebugAdapter', + 'content', + 'contextDidChange', + 'controller', + 'controllerFor', + 'controllerName', + 'copy', + 'create', + 'current-when', + 'currentPath', + 'currentRoute', + 'currentRouteName', + 'currentURL', + 'customEvents', + 'dasherize', + 'data', + 'deactivate', + 'debounce', + 'debug', + 'debugger', + 'decamelize', + 'decrementProperty', + 'defer', + 'deferReadiness', + 'defineProperty', + 'delete', + 'deleteMeta', + 'denodeify', + 'deprecate', + 'deprecateFunc', + 'deprecateProperty', + 'deprecatingAlias', + 'describe', + 'descriptorFor', + 'deserialize', + 'deserializeQueryParam', + 'destroy', + 'detect', + 'didBecomeReady', + 'didInsertElement', + 'didReceiveAttrs', + 'didRender', + 'didTransition', + 'didUpdate', + 'didUpdateAttrs', + 'disabled', + 'disabledClass', + 'disconnectOutlet', + 'document', + 'domReady', + 'each', + 'each-in', + 'eachComputedProperty', + 'element', + 'elementId', + 'empty', + 'end', + 'endPropertyChanges', + 'engine', + 'ensureInitializers', + 'enter', + 'equal', + 'error', + 'eventDispatcher', + 'eventName', + 'events', + 'every', + 'exception', + 'exit', + 'expandLocalLookup', + 'expandProperties', + 'extend', + 'factoryFor', + 'fallback', + 'fillIn', + 'filter', + 'filterBy', + 'finally', + 'find', + 'findBy', + 'findElementInParentElement', + 'findModel', + 'findWithAssert', + 'firstObject', + 'focusIn', + 'focusOut', + 'followRedirects', + 'forEach', + 'formatURL', + 'from', + 'fullRouteName', + 'generateController', + 'generateControllerFactory', + 'generateGuid', + 'get', + 'getChildViews', + 'getEach', + 'getEngineParent', + 'getFilters', + 'getHash', + 'getModelTypes', + 'getOwner', + 'getProperties', + 'getRecordColor', + 'getRecordColumnValues', + 'getRecordFilterValues', + 'getRecordKeywords', + 'getRecords', + 'getRootViews', + 'getState', + 'getURL', + 'getViewBoundingClientRect', + 'getViewBounds', + 'getViewClientRects', + 'getViewElement', + 'getViewId', + 'getViewRange', + 'getWithDefault', + 'global', + 'gt', + 'gte', + 'guidFor', + 'handleEvent', + 'handleURL', + 'has', + 'hasArrayObservers', + 'hasListeners', + 'hasObserverFor', + 'hasRegistration', + 'hasRoute', + 'hash', + 'hashSettled', + 'helper', + 'helperContainer', + 'history', + 'href', + 'htmlSafe', + 'if', + 'includes', + 'incrementProperty', + 'indexOf', + 'info', + 'init', + 'initState', + 'initializer', + 'inject', + 'injectTestHelpers', + 'injection', + 'input', + 'insertAt', + 'insertNewline', + 'inspect', + 'instanceInitializer', + 'instrument', + 'intermediateTransitionTo', + 'intersect', + 'invoke', + 'isActive', + 'isActiveIntent', + 'isAny', + 'isArray', + 'isBlank', + 'isBrowser', + 'isDestroyed', + 'isDestroying', + 'isEmpty', + 'isEnabled', + 'isEqual', + 'isEvery', + 'isFulfilled', + 'isHTMLSafe', + 'isInteractive', + 'isNone', + 'isObject', + 'isPending', + 'isPresent', + 'isRejected', + 'isSettled', + 'isVisible', + 'jQuery', + 'join', + 'keyDown', + 'keyEvent', + 'keyPress', + 'keyUp', + 'knownForType', + 'lastIndexOf', + 'lastObject', + 'later', + 'layout', + 'layoutName', + 'length', + 'let', + 'link-to', + 'loading', + 'loadingClass', + 'loadingHref', + 'loc', + 'localName', + 'location', + 'log', + 'lookup', + 'lookupDescription', + 'lt', + 'lte', + 'makeArray', + 'makeToString', + 'map', + 'mapBy', + 'match', + 'matches', + 'max', + 'merge', + 'mergedProperties', + 'meta', + 'metaForProperty', + 'method', + 'min', + 'mixin', + 'model', + 'modelFor', + 'mount', + 'mut', + 'name', + 'namespace', + 'nearestOfType', + 'nearestWithProperty', + 'next', + 'none', + 'normalize', + 'normalizeFullName', + 'not', + 'notEmpty', + 'notifyPropertyChange', + 'objectAt', + 'objectAtContent', + 'objectsAt', + 'observeModelType', + 'observer', + 'observerRecord', + 'observes', + 'off', + 'on', + 'onInjectHelpers', + 'onLoad', + 'onUpdateURL', + 'once', + 'one', + 'oneWay', + 'options', + 'optionsForType', + 'or', + 'originalMethods', + 'outlet', + 'ownerInjection', + 'paramNames', + 'params', + 'paramsFor', + 'parent', + 'parentView', + 'parentViewDidChange', + 'parseName', + 'partial', + 'pattern', + 'pauseTest', + 'popObject', + 'positionalParams', + 'promise', + 'property', + 'propertyDidChange', + 'propertyWillChange', + 'pushObject', + 'pushObjects', + 'pushState', + 'query-params', + 'queryParams', + 'queryParamsDidChange', + 'queues', + 'race', + 'readDOMAttr', + 'readOnly', + 'readonly', + 'reads', + 'ready', + 'reason', + 'recognize', + 'recognizeAndLoad', + 'recompute', + 'redirect', + 'reduce', + 'refresh', + 'register', + 'registerAsyncHelper', + 'registerDeprecationHandler', + 'registerHelper', + 'registerOptions', + 'registerOptionsForType', + 'registerWaiter', + 'registerWarnHandler', + 'registeredActions', + 'registeredOption', + 'registeredOptions', + 'registeredOptionsForType', + 'registrations', + 'registry', + 'reject', + 'rejectBy', + 'rel', + 'releaseMethods', + 'removeArrayObserver', + 'removeAt', + 'removeListener', + 'removeObject', + 'removeObjects', + 'removeObserver', + 'removeTestHelpers', + 'render', + 'renderTemplate', + 'reopen', + 'reopenClass', + 'replace', + 'replaceContent', + 'replaceRoute', + 'replaceState', + 'replaceURL', + 'replaceWith', + 'rerender', + 'reset', + 'resetController', + 'resolve', + 'resolveController', + 'resolveHelper', + 'resolveModel', + 'resolveOther', + 'resolveRegistration', + 'resolveRoute', + 'resolveTemplate', + 'resolveView', + 'resolver', + 'resolverFor', + 'resumeTest', + 'rethrow', + 'retry', + 'reverseObjects', + 'rootElement', + 'rootURL', + 'routeDidChange', + 'routeName', + 'routeWillChange', + 'run', + 'runInDebug', + 'runInitializers', + 'runInstanceInitializers', + 'runLoadHooks', + 'schedule', + 'scheduleOnce', + 'send', + 'sendAction', + 'sendEvent', + 'serialize', + 'serializeQueryParam', + 'serializeQueryParamKey', + 'set', + 'setDiff', + 'setEach', + 'setEngineParent', + 'setObjects', + 'setOwner', + 'setProperties', + 'setURL', + 'setup', + 'setupController', + 'setupForTesting', + 'setupHandler', + 'setupRegistry', + 'shiftObject', + 'shouldRender', + 'size', + 'slice', + 'sort', + 'sortBy', + 'startRouting', + 'store', + 'subscribe', + 'sum', + 'tabindex', + 'tagName', + 'target', + 'teardownViews', + 'templateName', + 'testCheckboxClick', + 'testHelpers', + 'testing', + 'textarea', + 'then', + 'throttle', + 'title', + 'to', + 'toArray', + 'toString', + 'toggleProperty', + 'transitionTo', + 'transitionToRoute', + 'translateToContainerFullname', + 'trigger', + 'triggerAction', + 'triggerEvent', + 'tryInvoke', + 'trySet', + 'type', + 'typeInjection', + 'typeOf', + 'unbound', + 'underscore', + 'union', + 'uniq', + 'uniqBy', + 'unless', + 'unregister', + 'unregisterHelper', + 'unregisterWaiter', + 'unshiftObject', + 'unshiftObjects', + 'unsubscribe', + 'unwatch', + 'url', + 'urlFor', + 'useRouterNaming', + 'userAgent', + 'validationCache', + 'value', + 'visit', + 'volatile', + 'w', + 'wait', + 'waitForDOMReady', + 'warn', + 'watch', + 'watchModelTypes', + 'watchRecords', + 'willClearRender', + 'willDestroy', + 'willDestroyElement', + 'willInsertElement', + 'willRender', + 'willTransition', + 'willUpdate', + 'with', + 'without', + 'wrap', + 'wrapModelType', + 'wrapRecord', + 'yield', + ], +};