UNPKG

polymer-analyzer

Version:
524 lines (522 loc) 20.3 kB
"use strict"; /** * @license * Copyright (c) 2016 The Polymer Project Authors. All rights reserved. * This code may only be used under the BSD style license found at * http://polymer.github.io/LICENSE.txt * The complete set of authors may be found at * http://polymer.github.io/AUTHORS.txt * The complete set of contributors may be found at * http://polymer.github.io/CONTRIBUTORS.txt * Code distributed by Google as part of the polymer project is also * subject to an additional IP rights grant found at * http://polymer.github.io/PATENTS.txt */ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const chai_1 = require("chai"); const path = require("path"); const class_scanner_1 = require("../../javascript/class-scanner"); const javascript_parser_1 = require("../../javascript/javascript-parser"); const polymer_element_1 = require("../../polymer/polymer-element"); const fs_url_loader_1 = require("../../url-loader/fs-url-loader"); const test_utils_1 = require("../test-utils"); chai_1.use(require('chai-subset')); suite('Polymer2ElementScanner', () => { const testFilesDir = path.resolve(__dirname, '../static/polymer2/'); const urlLoader = new fs_url_loader_1.FSUrlLoader(testFilesDir); const underliner = new test_utils_1.CodeUnderliner(urlLoader); function getElements(filename) { return __awaiter(this, void 0, void 0, function* () { const file = yield urlLoader.load(filename); const parser = new javascript_parser_1.JavaScriptParser(); const document = parser.parse(file, filename); const scanner = new class_scanner_1.ClassScanner(); const visit = (visitor) => Promise.resolve(document.visit([visitor])); const { features } = yield scanner.scan(document, visit); return features.filter((e) => e instanceof polymer_element_1.ScannedPolymerElement); }); } ; function getTestProps(element) { return __awaiter(this, void 0, void 0, function* () { const props = { className: element.className, superClass: element.superClass && element.superClass.identifier, tagName: element.tagName, description: element.description, summary: element.summary, properties: yield Promise.all(Array.from(element.properties.values()).map((p) => __awaiter(this, void 0, void 0, function* () { const result = { name: p.name, description: p.description }; if (p.type) { result.type = p.type; } if (p.observerExpression) { result.propertiesInObserver = p.observerExpression.properties.map((p) => p.name); } if (p.computedExpression) { result.propertiesInComputed = p.computedExpression.properties.map((p) => p.name); } if (p.warnings.length > 0) { result.warningUnderlines = yield underliner.underline(p.warnings); } return result; }))), attributes: Array.from(element.attributes.values()).map((a) => ({ name: a.name, })), methods: Array.from(element.methods.values()) .map((m) => ({ name: m.name, params: m.params, return: m.return, description: m.description })), warningUnderlines: yield underliner.underline(element.warnings), }; if (element.observers.length > 0) { props.observers = element.observers.map((o) => o.expression); props.observerProperties = element.observers.filter((o) => o.parsedExpression) .map((o) => o.parsedExpression.properties.map((p) => p.name)); } if (element.mixins.length > 0) { props.mixins = element.mixins.map((m) => m.identifier); } return props; }); } test('Finds two basic elements', () => __awaiter(this, void 0, void 0, function* () { const elements = yield getElements('test-element-1.js'); const elementData = yield Promise.all(elements.map(getTestProps)); chai_1.assert.deepEqual(elementData, [ { tagName: 'test-element', className: 'TestElement', superClass: 'Polymer.Element', description: '', summary: '', properties: [{ name: 'foo', description: 'The foo prop.', type: '(m-test|function)', }], attributes: [{ name: 'foo', }], methods: [], warningUnderlines: [], }, { tagName: undefined, className: 'BaseElement', superClass: 'Polymer.Element', description: 'A very basic element', summary: 'A basic element', properties: [{ name: 'foo', description: 'A base foo element.', type: 'string' }], attributes: [{ name: 'foo', }], methods: [], warningUnderlines: [], }, ]); const underlinedSource1 = yield underliner.underline(elements[0].sourceRange); chai_1.assert.equal(underlinedSource1, ` class TestElement extends Polymer.Element { ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ static get properties() { ~~~~~~~~~~~~~~~~~~~~~~~~~~~ return { ~~~~~~~~~~~~ /** ~~~~~~~~~ * The foo prop. ~~~~~~~~~~~~~~~~~~~~~~ * @public ~~~~~~~~~~~~~~~~ * @type {m-test|function} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ ~~~~~~~~~ foo: { ~~~~~~~~~~~~ notify: true, ~~~~~~~~~~~~~~~~~~~~~ type: String, ~~~~~~~~~~~~~~~~~~~~~ } ~~~~~~~ } ~~~~~ } ~~~ } ~`); const underlinedSource2 = yield underliner.underline(elements[1].sourceRange); chai_1.assert.equal(underlinedSource2, ` class BaseElement extends Polymer.Element { ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ static get properties() { ~~~~~~~~~~~~~~~~~~~~~~~~~~~ return { ~~~~~~~~~~~~ /** A base foo element. */ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ foo: { ~~~~~~~~~~~~ notify: true, ~~~~~~~~~~~~~~~~~~~~~ type: String, ~~~~~~~~~~~~~~~~~~~~~ }, ~~~~~~~~ }; ~~~~~~ } ~~~ } ~`); })); test('Uses static is getter for tagName', () => __awaiter(this, void 0, void 0, function* () { const elements = yield getElements('test-element-2.js'); const elementData = yield Promise.all(elements.map(getTestProps)); chai_1.assert.deepEqual(elementData, [ { tagName: 'test-element', className: 'TestElement', superClass: 'HTMLElement', description: '', summary: '', properties: [], attributes: [], methods: [], warningUnderlines: [], }, ]); })); test('Finds vanilla elements', () => __awaiter(this, void 0, void 0, function* () { const elements = yield getElements('test-element-4.js'); const elementData = yield Promise.all(elements.map(getTestProps)); chai_1.assert.deepEqual(elementData, [ { tagName: 'test-element', className: 'TestElement', superClass: 'HTMLElement', description: '', summary: '', properties: [], attributes: [ { name: 'a', }, { name: 'b', } ], methods: [], warningUnderlines: [], }, ]); })); test('Observed attributes override induced attributes', () => __awaiter(this, void 0, void 0, function* () { const elements = yield getElements('test-element-5.js'); const elementData = yield Promise.all(elements.map(getTestProps)); chai_1.assert.deepEqual(elementData, [ { tagName: 'test-element', className: 'TestElement', superClass: 'Polymer.Element', description: '', summary: '', properties: [{ name: 'foo', description: '', type: 'string', }], attributes: [ { name: 'a', }, { name: 'b', } ], methods: [], warningUnderlines: [], }, ]); })); test('properly sets className for elements with the memberof tag', () => __awaiter(this, void 0, void 0, function* () { const elements = yield getElements('test-element-8.js'); const elementData = yield Promise.all(elements.map(getTestProps)); chai_1.assert.deepEqual(elementData, [ { tagName: 'test-element-one', className: 'Polymer.TestElementOne', superClass: 'Polymer.Element', description: `This element is a member of Polymer namespace and is registered with its namespaced name.`, summary: '', properties: [{ name: 'foo', description: '', type: 'string', }], attributes: [{ name: 'foo', }], methods: [], warningUnderlines: [], }, { tagName: 'test-element-two', className: 'Polymer.TestElementTwo', superClass: 'Polymer.Element', description: `This element is a member of Polymer namespace and is registered without its namespaced name.`, summary: '', properties: [{ name: 'foo', description: '', type: 'string', }], attributes: [{ name: 'foo', }], methods: [], warningUnderlines: [], } ]); })); test('Read @appliesMixin annotations', () => __awaiter(this, void 0, void 0, function* () { const elements = yield getElements('test-element-6.js'); const elementData = yield Promise.all(elements.map(getTestProps)); chai_1.assert.deepEqual(elementData, [ { tagName: 'test-element', className: 'TestElement', superClass: 'Polymer.Element', description: '', summary: '', properties: [], attributes: [], methods: [], mixins: ['Mixin2', 'Mixin1'], warningUnderlines: [], }, ]); })); test('Reads just @appliesMixin annotation', () => __awaiter(this, void 0, void 0, function* () { const elements = yield getElements('test-element-9.js'); const elementData = yield Promise.all(elements.map(getTestProps)); chai_1.assert.deepEqual(elementData, [ { tagName: undefined, className: 'BaseElement', superClass: 'Polymer.Element', description: '', summary: '', properties: [], attributes: [], methods: [], warningUnderlines: [], }, { tagName: undefined, className: 'SubElement', superClass: 'BaseElement', description: '', summary: '', properties: [], attributes: [], methods: [], mixins: ['Mixin'], warningUnderlines: [], }, { tagName: undefined, className: 'SubElement2', superClass: 'BaseElement', description: '', summary: '', properties: [], attributes: [], methods: [], mixins: ['Mixin'], warningUnderlines: [], }, { tagName: undefined, className: 'window.MyElement', superClass: 'MixedElement', description: '', summary: '', properties: [], attributes: [], methods: [], mixins: ['MyMixin'], warningUnderlines: [], } ]); })); test('properly reads properties and methods of elements and element classes', () => __awaiter(this, void 0, void 0, function* () { const elements = yield getElements('test-element-10.js'); const elementData = yield Promise.all(elements.map(getTestProps)); chai_1.assert.deepEqual(elementData, [ { tagName: 'test-element', className: 'TestElement', superClass: 'Polymer.Element', description: ``, summary: '', properties: [{ name: 'foo', description: '', type: 'string', }], attributes: [{ name: 'foo', }], methods: [ { name: 'customInstanceFunction', description: '', params: [], return: undefined }, { name: 'customInstanceFunctionWithJSDoc', description: 'This is the description for ' + 'customInstanceFunctionWithJSDoc.', params: [], return: { desc: 'The number 5, always.', type: 'Number', }, }, { name: 'customInstanceFunctionWithParams', description: '', params: [ { name: 'a', type: undefined, description: undefined }, { name: 'b', type: undefined, description: undefined }, { name: 'c', type: undefined, description: undefined } ], return: undefined, }, { name: 'customInstanceFunctionWithParamsAndJSDoc', description: 'This is the description for ' + 'customInstanceFunctionWithParamsAndJSDoc.', params: [ { name: 'a', type: 'Number', description: 'The first argument', }, { name: 'b', type: 'Number', description: undefined }, { name: 'c', type: 'Number', description: 'The third argument', } ], return: { desc: 'The number 7, always.', type: 'Number', }, }, { name: 'customInstanceFunctionWithParamsAndPrivateJSDoc', description: 'This is the description for\n' + 'customInstanceFunctionWithParamsAndPrivateJSDoc.', params: [], return: undefined, }, ], warningUnderlines: [], }, ]); })); test('warns for bad observers and computed properties', () => __awaiter(this, void 0, void 0, function* () { const elements = yield getElements('test-element-12.js'); const elementData = yield Promise.all(elements.map(getTestProps)); chai_1.assert.deepEqual(elementData, [{ attributes: [{ name: 'parse-error' }, { name: 'bad-kind-of-expression' }], className: 'TestElement', description: '', methods: [], properties: [ { name: 'parseError', type: 'string', description: '', warningUnderlines: [ ` computed: 'let let let', ~`, ` observer: 'let let let', ~`, ] }, { name: 'badKindOfExpression', type: 'string', description: '', propertiesInComputed: ['foo'], propertiesInObserver: ['foo', 'bar', 'baz'], warningUnderlines: [ ` computed: 'foo', ~~~`, ` observer: 'foo(bar, baz)' ~~~~~~~~~~~~~`, ] } ], summary: '', superClass: 'Polymer.Element', tagName: 'test-element', warningUnderlines: [ ` 'let let let parseError', ~`, ` 'foo', ~~~` ], observers: [ 'let let let parseError', 'foo', 'foo(bar)', ], observerProperties: [['foo'], ['foo', 'bar']], }]); })); test('can identify elements registered with ClassName.is', () => __awaiter(this, void 0, void 0, function* () { const elements = yield getElements('test-element-11.js'); const elementData = yield Promise.all(elements.map(getTestProps)); chai_1.assert.deepEqual(elementData, [{ attributes: [{ name: 'prop1' }], className: 'MyElement', description: '', methods: [], properties: [{ name: 'prop1', description: '', type: 'string' }], summary: '', superClass: 'Polymer.Element', tagName: 'my-app', warningUnderlines: [], }]); })); }); //# sourceMappingURL=polymer2-element-scanner_test.js.map