From 18867e2ff30aba189b3e0bf204dc5bc79c81e7a8 Mon Sep 17 00:00:00 2001 From: Alexander Marks Date: Mon, 16 Oct 2017 16:30:23 -0700 Subject: [PATCH] Add integration tests for some representative Polymer libraries. To add a new repo with golden output, edit `scripts/fixtures.txt` and run `npm run test:setup && npm run test:make-goldens`. --- .gitignore | 3 +- package-lock.json | 15 + package.json | 9 +- scripts/fixtures.txt | 3 + scripts/make-goldens.js | 39 +++ scripts/setup-fixtures.sh | 30 ++ src/test/gen-ts_test.ts | 31 ++ .../goldens/paper-behaviors/expected.d.ts | 4 + src/test/goldens/paper-button/expected.d.ts | 4 + src/test/goldens/polymer/expected.d.ts | 271 ++++++++++++++++++ tsconfig.json | 3 +- 11 files changed, 408 insertions(+), 4 deletions(-) create mode 100644 scripts/fixtures.txt create mode 100755 scripts/make-goldens.js create mode 100755 scripts/setup-fixtures.sh create mode 100644 src/test/gen-ts_test.ts create mode 100644 src/test/goldens/paper-behaviors/expected.d.ts create mode 100644 src/test/goldens/paper-button/expected.d.ts create mode 100644 src/test/goldens/polymer/expected.d.ts diff --git a/.gitignore b/.gitignore index fd5a09e4f..8de5704b0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules -lib/ \ No newline at end of file +lib +src/test/fixtures diff --git a/package-lock.json b/package-lock.json index ddefdaaaa..87454c6f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -181,6 +181,12 @@ "integrity": "sha1-muuabF6IY4qtFx4Wf1kAq+JINdA=", "dev": true }, + "bower": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/bower/-/bower-1.8.2.tgz", + "integrity": "sha1-rfU1KcjUrwLvJPuNU0HBQZ0z4vc=", + "dev": true + }, "brace-expansion": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", @@ -2162,6 +2168,15 @@ "path-parse": "1.0.5" } }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "7.1.2" + } + }, "safe-buffer": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", diff --git a/package.json b/package.json index 036997c14..130012aa6 100644 --- a/package.json +++ b/package.json @@ -19,9 +19,11 @@ "devDependencies": { "@types/chai": "^4.0.4", "@types/mocha": "^2.2.43", + "bower": "^1.8.2", "chai": "^4.1.2", "clang-format": "^1.0.55", "mocha": "^3.5.3", + "rimraf": "^2.6.2", "source-map-support": "^0.5.0", "typescript": "^2.5.3", "watchy": "^0.7.0" @@ -31,8 +33,11 @@ "format": "find src -name '*.ts' | xargs clang-format --style=file -i", "build": "npm run clean && tsc", "build:watch": "tsc --watch", - "test": "npm run build && mocha", - "test:watch": "watchy -w src -- npm run test" + "test": "npm run test:setup-once && npm run build && mocha", + "test:watch": "watchy -w src -- npm run test", + "test:setup": "scripts/setup-fixtures.sh", + "test:setup-once": "mkdir src/test/fixtures && npm run test:setup || true", + "test:make-goldens": "scripts/make-goldens.js" }, "repository": { "type": "git", diff --git a/scripts/fixtures.txt b/scripts/fixtures.txt new file mode 100644 index 000000000..3947b825a --- /dev/null +++ b/scripts/fixtures.txt @@ -0,0 +1,3 @@ +https://github.com/PolymerElements/paper-button.git v2.0.0 paper-button +https://github.com/PolymerElements/paper-behaviors.git v2.0.1 paper-behaviors +https://github.com/Polymer/polymer.git v2.1.1 polymer diff --git a/scripts/make-goldens.js b/scripts/make-goldens.js new file mode 100755 index 000000000..08b4a5f9d --- /dev/null +++ b/scripts/make-goldens.js @@ -0,0 +1,39 @@ +#!/usr/bin/env node + +/** + * @license + * Copyright (c) 2017 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 + */ + +/** + * This script should be run from `npm run test:make-goldens`. It runs the + * TypeScript declarations generator across all repos in the + * `test/src/fixtures` directory, and writes the output to + * `test/src/goldens//expected.d.ts`. The results of this script + * *should* be checked in. + */ + +const fs = require('fs'); +const path = require('path'); +const rimraf = require('rimraf'); +const {generateDeclarations} = require('../lib/gen-ts'); + +const fixturesDir = path.join(__dirname, '..', 'src', 'test', 'fixtures'); +const goldensDir = path.join(__dirname, '..', 'src', 'test', 'goldens'); + +rimraf.sync(goldensDir); +fs.mkdirSync(goldensDir); + +for (const dir of fs.readdirSync(fixturesDir)) { + generateDeclarations(path.join(fixturesDir, dir)).then((declarations) => { + const outDir = path.join(goldensDir, dir); + fs.mkdirSync(outDir); + fs.writeFileSync(path.join(outDir, 'expected.d.ts'), declarations); + }); +} diff --git a/scripts/setup-fixtures.sh b/scripts/setup-fixtures.sh new file mode 100755 index 000000000..01d5cc4ba --- /dev/null +++ b/scripts/setup-fixtures.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash + +# Copyright (c) 2017 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 + +# This script should be run from `npm run test:setup`. It reads a list of repos +# from `fixtures.txt`, clones them into the `src/test/fixtures` directory, and +# installs them for testing. +# +# To add a new repo for testing, add it to `fixtures.txt`, run `npm run +# test:setup && npm run test:make-goldens`. + +set -e + +cd src/test +rm -rf fixtures +mkdir fixtures +cd fixtures + +while read -r repo tag dir; do + git clone $repo --branch $tag --single-branch --depth 1 $dir + cd $dir + bower install + cd - +done < ../../../scripts/fixtures.txt diff --git a/src/test/gen-ts_test.ts b/src/test/gen-ts_test.ts new file mode 100644 index 000000000..8e2e48d3f --- /dev/null +++ b/src/test/gen-ts_test.ts @@ -0,0 +1,31 @@ +/** + * @license + * Copyright (c) 2017 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 + */ + +import {assert} from 'chai'; +import * as fs from 'fs'; +import * as path from 'path'; + +import {generateDeclarations} from '../gen-ts'; + +const fixtures = path.join(__dirname, '..', '..', 'src', 'test', 'fixtures'); +const goldens = path.join(__dirname, '..', '..', 'src', 'test', 'goldens'); + +suite('generateDeclarations', () => { + for (const fixture of fs.readdirSync(goldens)) { + test(fixture, async () => { + const golden = + fs.readFileSync(path.join(goldens, fixture, 'expected.d.ts')); + const declarations = + await generateDeclarations(path.join(fixtures, fixture)); + assert.equal(declarations, golden.toString()); + }).timeout(30000); // These tests can take a long time. + } +}); diff --git a/src/test/goldens/paper-behaviors/expected.d.ts b/src/test/goldens/paper-behaviors/expected.d.ts new file mode 100644 index 000000000..c428464c5 --- /dev/null +++ b/src/test/goldens/paper-behaviors/expected.d.ts @@ -0,0 +1,4 @@ +declare namespace Polymer { + type Constructor = new(...args: any[]) => T; + +} \ No newline at end of file diff --git a/src/test/goldens/paper-button/expected.d.ts b/src/test/goldens/paper-button/expected.d.ts new file mode 100644 index 000000000..c428464c5 --- /dev/null +++ b/src/test/goldens/paper-button/expected.d.ts @@ -0,0 +1,4 @@ +declare namespace Polymer { + type Constructor = new(...args: any[]) => T; + +} \ No newline at end of file diff --git a/src/test/goldens/polymer/expected.d.ts b/src/test/goldens/polymer/expected.d.ts new file mode 100644 index 000000000..b80d2c7fa --- /dev/null +++ b/src/test/goldens/polymer/expected.d.ts @@ -0,0 +1,271 @@ +declare namespace Polymer { + type Constructor = new(...args: any[]) => T; + + Polymer.dedupingMixin(mixin: T): any; + + interface PropertyAccessors { + attributeChangedCallback(name: string, old: ?string|null, value: ?string|null): any; + _initializeProperties(): any; + _initializeProtoProperties(props: Object): any; + _initializeInstanceProperties(props: Object): any; + _ensureAttribute(attribute: string, value: string): any; + _attributeToProperty(attribute: string, value: ?string|null, type?: any): any; + _propertyToAttribute(property: string, attribute?: string, value?: any): any; + _valueToNodeAttribute(node: Element, value: any, attribute: string): any; + _serializeValue(value: any): (string|undefined); + _deserializeValue(value: ?string|null, type?: any): any; + _createPropertyAccessor(property: string, readOnly?: boolean): any; + _hasAccessor(property: string): boolean; + _setProperty(property: string, value: any): any; + _setPendingProperty(property: string, value: any): boolean; + _isPropertyPending(prop: string): boolean; + _invalidateProperties(): any; + _enableProperties(): any; + _flushProperties(): any; + ready(): any; + _propertiesChanged(currentProps: !Object, changedProps: !Object, oldProps: !Object): any; + _shouldPropertyChange(property: string, value: any, old: any): boolean; + } + const PropertyAccessors: >(base: T) => T & Constructor; + + Polymer.setRootPath(path: string): any; + + Polymer.setSanitizeDOMValue(newSanitizeDOMValue: ((function (*, string, string, Node): *)|undefined)): any; + + Polymer.setPassiveTouchGestures(usePassive: boolean): any; + + class DomModule extends HTMLElement { + attributeChangedCallback(name: any, old: any, value: any): any; + register(id?: string): any; + } + + interface TemplateStamp { + _stampTemplate(template: !HTMLTemplateElement): !StampedTemplate; + _addMethodEventListenerToNode(node: Node, eventName: string, methodName: string, context?: any): Function; + _addEventListenerToNode(node: Node, eventName: string, handler: Function): any; + _removeEventListenerFromNode(node: Node, eventName: string, handler: Function): any; + } + const TemplateStamp: >(base: T) => T & Constructor; + + interface PropertyEffects extends TemplateStamp, PropertyAccessors{ + _stampTemplate(template: !HTMLTemplateElement): !StampedTemplate; + _initializeProperties(): any; + _initializeProtoProperties(props: Object): any; + _initializeInstanceProperties(props: Object): any; + _setProperty(property: any, value: any): any; + _setPendingProperty(property: string, value: any, shouldNotify?: boolean): boolean; + _invalidateProperties(): any; + ready(): any; + _propertiesChanged(currentProps: any, changedProps: any, oldProps: any): any; + _addPropertyEffect(property: string, type: string, effect?: Object): any; + _removePropertyEffect(property: string, type: string, effect?: Object): any; + _hasPropertyEffect(property: string, type?: string): boolean; + _hasReadOnlyEffect(property: string): boolean; + _hasNotifyEffect(property: string): boolean; + _hasReflectEffect(property: string): boolean; + _hasComputedEffect(property: string): boolean; + _setPendingPropertyOrPath(path: (string|!Array.<(number|string)>), value: any, shouldNotify?: boolean, isPathNotification?: boolean): boolean; + _setUnmanagedPropertyToNode(node: Node, prop: string, value: any): any; + _enqueueClient(client: Object): any; + _flushClients(): any; + _readyClients(): any; + setProperties(props: Object, setReadOnly?: boolean): any; + _propagatePropertyChanges(changedProps: Object, oldProps: Object, hasPaths: boolean): any; + linkPaths(to: (string|!Array.<(string|number)>), from: (string|!Array.<(string|number)>)): any; + unlinkPaths(path: (string|!Array.<(string|number)>)): any; + notifySplices(path: string, splices: any[]): any; + get(path: (string|!Array.<(string|number)>), root?: Object): any; + set(path: (string|!Array.<(string|number)>), value: any, root?: Object): any; + push(path: (string|!Array.<(string|number)>), ...items: any): number; + pop(path: (string|!Array.<(string|number)>)): any; + splice(path: (string|!Array.<(string|number)>), start: number, deleteCount: number, ...items: any): any[]; + shift(path: (string|!Array.<(string|number)>)): any; + unshift(path: (string|!Array.<(string|number)>), ...items: any): number; + notifyPath(path: string, value?: any): any; + _createReadOnlyProperty(property: string, protectedSetter?: boolean): any; + _createPropertyObserver(property: string, methodName: string, dynamicFn?: boolean): any; + _createMethodObserver(expression: string, dynamicFn?: (boolean|Object)): any; + _createNotifyingProperty(property: string): any; + _createReflectedProperty(property: string): any; + _createComputedProperty(property: string, expression: string, dynamicFn?: (boolean|Object)): any; + _bindTemplate(template: HTMLTemplateElement, instanceBinding?: boolean): !TemplateInfo; + _removeBoundDom(dom: !StampedTemplate): any; + } + const PropertyEffects: >(base: T) => T & Constructor; + + interface ElementMixin extends PropertyEffects{ + _template: [object Object]; + _importPath: [object Object]; + rootPath: [object Object]; + importPath: [object Object]; + root: [object Object]; + $: [object Object]; + attributeChangedCallback(name: string, old: ?string|null, value: ?string|null): any; + _initializeProperties(): any; + ready(): any; + _readyClients(): any; + connectedCallback(): any; + disconnectedCallback(): any; + _attachDom(dom: StampedTemplate): ShadowRoot; + updateStyles(properties?: Object): any; + resolveUrl(url: string, base?: string): string; + } + const ElementMixin: >(base: T) => T & Constructor; + + class Element { + } + + interface GestureEventListeners { + _addEventListenerToNode(node: any, eventName: any, handler: any): any; + _removeEventListenerFromNode(node: any, eventName: any, handler: any): any; + } + const GestureEventListeners: >(base: T) => T & Constructor; + + Polymer.importHref(href: string, onload?: Function, onerror?: Function, optAsync?: boolean): HTMLLinkElement; + + Polymer.enqueueDebouncer(debouncer: Polymer.Debouncer): any; + + Polymer.flush(): any; + + Polymer.dom(obj: (!Node|Event)): (DomApi|EventApi); + + interface LegacyElementMixin extends ElementMixin, GestureEventListeners{ + isAttached: [object Object]; + _debouncers: [object Object]; + attributeChangedCallback(name: string, old: ?string|null, value: ?string|null): any; + _initializeProperties(): any; + ready(): any; + connectedCallback(): any; + disconnectedCallback(): any; + created(): any; + attached(): any; + detached(): any; + attributeChanged(name: string, old: ?string|null, value: ?string|null): any; + _registered(): any; + _ensureAttributes(): any; + _applyListeners(): any; + serialize(value: any): (string|undefined); + deserialize(value: string, type: any): any; + reflectPropertyToAttribute(property: string, attribute?: string, value?: any): any; + serializeValueToAttribute(value: any, attribute: string, node: Element): any; + extend(prototype: Object, api: Object): Object; + mixin(target: Object, source: Object): Object; + chainObject(object: Object, prototype: Object): Object; + instanceTemplate(template: HTMLTemplateElement): DocumentFragment; + fire(type: string, detail?: any, options?: {bubbles: (boolean|undefined), cancelable: (boolean|undefined), composed: (boolean|undefined)}): Event; + listen(node: Element, eventName: string, methodName: string): any; + unlisten(node: Element, eventName: string, methodName: string): any; + setScrollDirection(direction?: string, node?: Element): any; + $$(slctr: string): Element; + distributeContent(): any; + getEffectiveChildNodes(): Array.; + queryDistributedElements(selector: string): Array.; + getEffectiveChildren(): Array.; + getEffectiveTextContent(): string; + queryEffectiveChildren(selector: string): Object.; + queryAllEffectiveChildren(selector: string): Array.; + getContentChildNodes(slctr?: string): Array.; + getContentChildren(slctr?: string): Array.; + isLightDescendant(node: ?Node|null): boolean; + isLocalDescendant(node?: Element): boolean; + scopeSubtree(container: any, shouldObserve: any): any; + getComputedStyleValue(property: string): string; + debounce(jobName: string, callback: function (), wait: number): Object; + isDebouncerActive(jobName: string): boolean; + flushDebouncer(jobName: string): any; + cancelDebouncer(jobName: string): any; + async(callback: Function, waitTime?: number): number; + cancelAsync(handle: number): any; + create(tag: string, props: Object): Element; + importHref(href: string, onload: Function, onerror: Function, optAsync: boolean): HTMLLinkElement; + elementMatches(selector: string, node?: Element): boolean; + toggleAttribute(name: string, bool?: boolean, node?: Element): any; + toggleClass(name: string, bool?: boolean, node?: Element): any; + transform(transformText: string, node?: Element): any; + translate3d(x: number, y: number, z: number, node?: Element): any; + arrayDelete(arrayOrPath: (string|!Array.<(number|string)>), item: any): any[]; + _logger(level: string, args: any[]): any; + _log(...args: any): any; + _warn(...args: any): any; + _error(...args: any): any; + _logf(methodName: string, ...args: any): any[]; + } + const LegacyElementMixin: >(base: T) => T & Constructor; + + Polymer.mixinBehaviors(behaviors: !(Object|Array), klass: (!HTMLElement|function (new: HTMLElement))): function (new: HTMLElement); + + Polymer.Class(info: !PolymerInit): function (new: HTMLElement); + + interface MutableData { + _shouldPropertyChange(property: string, value: any, old: any): boolean; + } + const MutableData: >(base: T) => T & Constructor; + + interface OptionalMutableData { + mutableData: [object Object]; + _shouldPropertyChange(property: string, value: any, old: any): boolean; + } + const OptionalMutableData: >(base: T) => T & Constructor; + + class DomBind extends domBindBase { + attributeChangedCallback(): any; + connectedCallback(): any; + disconnectedCallback(): any; + render(): any; + } + + class DomRepeat extends domRepeatBase { + items: any[]; + as: [object Object]; + indexAs: [object Object]; + itemsIndexAs: [object Object]; + sort: [object Object]; + filter: [object Object]; + observe: [object Object]; + delay: [object Object]; + renderedItemCount: [object Object]; + initialCount: [object Object]; + targetFramerate: [object Object]; + _targetFrameTime: [object Object]; + disconnectedCallback(): any; + connectedCallback(): any; + render(): any; + _showHideChildren(hidden: any): any; + itemForElement(el: HTMLElement): any; + indexForElement(el: HTMLElement): any; + modelForElement(el: HTMLElement): TemplateInstanceBase; + } + + class DomIf extends Polymer.Element { + if: [object Object]; + restamp: [object Object]; + connectedCallback(): any; + disconnectedCallback(): any; + render(): any; + _showHideChildren(): any; + } + + interface ArraySelectorMixin extends ElementMixin{ + items: any[]; + multi: [object Object]; + selected: [object Object]; + selectedItem: [object Object]; + toggle: [object Object]; + clearSelection(): any; + isSelected(item: any): boolean; + isIndexSelected(idx: number): boolean; + deselect(item: any): any; + deselectIndex(idx: number): any; + select(item: any): any; + selectIndex(idx: number): any; + } + const ArraySelectorMixin: >(base: T) => T & Constructor; + + class ArraySelector extends baseArraySelector { + } + + class CustomStyle extends HTMLElement { + getStyle(): HTMLStyleElement; + } + +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 4a0785e39..14bd0d021 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -24,6 +24,7 @@ "src/**/*.ts" ], "exclude": [ - "node_modules" + "node_modules", + "src/test/goldens" ] }