Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AF-2799: Remove mirrors from tests #219

Merged
merged 4 commits into from
Dec 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 52 additions & 53 deletions test/over_react/component/dom_components_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,63 +14,62 @@

library dom_components_test;

// Tell dart2js that this library only needs to reflect the specified types.
// This speeds up compilation and makes JS output much smaller.
@MirrorsUsed(targets: const [
'over_react.dom_components.Dom'
])
import 'dart:mirrors';

import 'package:over_react/over_react.dart';
import 'package:react/react_client.dart';
import 'package:test/test.dart';

main() {
group('Dom component:', () {
const expectedStaticMethods = const<Symbol>[
// DOM
#a, #abbr, #address, #area, #article, #aside, #audio, #b, #base, #bdi, #bdo, #big, #blockquote, #body, #br,
#button, #canvas, #caption, #cite, #code, #col, #colgroup, #data, #datalist, #dd, #del, #details, #dfn, #dialog,
#div, #dl, #dt, #em, #embed, #fieldset, #figcaption, #figure, #footer, #form, #h1, #h2, #h3, #h4, #h5, #h6,
#head, #header, #hr, #html, #i, #iframe, #img, #input, #ins, #kbd, #keygen, #label, #legend, #li, #link, #main,
#map, #mark, #menu, #menuitem, #meta, #meter, #nav, #noscript, #object, #ol, #optgroup, #option, #output, #p,
#param, #picture, #pre, #progress, #q, #rp, #rt, #ruby, #s, #samp, #script, #section, #select, #small, #source,
#span, #strong, #style, #sub, #summary, #sup, #table, #tbody, #td, #textarea, #tfoot, #th, #thead, #time,
#title, #tr, #track, #u, #ul, #variable, #video, #wbr,
// SVG
#svgA, #altGlyph, #altGlyphDef, #altGlyphItem, #animate, #animateColor, #animateMotion, #animateTransform,
#svgAudio, #svgCanvas, #circle, #clipPath, #colorProfile, #cursor, #defs, #desc, #discard, #ellipse, #feBlend,
#feColorMatrix, #feComponentTransfer, #feComposite, #feConvolveMatrix, #feDiffuseLighting, #feDisplacementMap,
#feDistantLight, #feDropShadow, #feFlood, #feFuncA, #feFuncB, #feFuncG, #feFuncR, #feGaussianBlur, #feImage,
#feMerge, #feMergeNode, #feMorphology, #feOffset, #fePointLight, #feSpecularLighting, #feSpotLight, #feTile,
#feTurbulence, #filter, #font, #fontFace, #fontFaceFormat, #fontFaceName, #fontFaceSrc, #fontFaceUri,
#foreignObject, #g, #glyph, #glyphRef, #hatch, #hatchpath, #hkern, #svgIframe, #image, #line, #linearGradient,
#marker, #mask, #mesh, #meshgradient, #meshpatch, #meshrow, #metadata, #missingGlyph, #mpath, #path, #pattern,
#polygon, #polyline, #radialGradient, #rect, #svgScript, #svgSet, #solidcolor, #stop, #svgStyle, #svg,
#svgSwitch, #symbol, #text, #textPath, #svgTitle, #tref, #tspan, #unknown, #use, #svgVideo, #view, #vkern,
];

List<Symbol> methods = [];
ClassMirror domClassMirror;

setUpAll(() {
domClassMirror = reflectClass(Dom);

// staticMembers is not implemented for the DDC and will throw is this test is loaded even if it's not run.
try {
methods = domClassMirror.staticMembers.values.map((m) => m.simpleName).toList();
var expectedMethodsOnDom = <Function /* dom method */, String /* method name */>{
Dom.a: 'a', Dom.abbr: 'abbr', Dom.address: 'address', Dom.area: 'area', Dom.article: 'article',
Dom.aside: 'aside', Dom.audio: 'audio', Dom.b: 'b', Dom.base: 'base', Dom.bdi: 'bdi', Dom.bdo: 'bdo',
Dom.big: 'big', Dom.blockquote: 'blockquote', Dom.body: 'body', Dom.br: 'br', Dom.button: 'button',
Dom.canvas: 'canvas', Dom.caption: 'caption', Dom.cite: 'cite', Dom.code: 'code', Dom.col: 'col',
Dom.colgroup: 'colgroup', Dom.data: 'data', Dom.datalist: 'datalist', Dom.dd: 'dd', Dom.del: 'del',
Dom.details: 'details', Dom.dfn: 'dfn', Dom.dialog: 'dialog', Dom.div: 'div', Dom.dl: 'dl', Dom.dt: 'dt',
Dom.em: 'em', Dom.embed: 'embed', Dom.fieldset: 'fieldset', Dom.figcaption: 'figcaption', Dom.figure: 'figure',
Dom.footer: 'footer', Dom.form: 'form', Dom.h1: 'h1', Dom.h2: 'h2', Dom.h3: 'h3', Dom.h4: 'h4', Dom.h5: 'h5',
Dom.h6: 'h6', Dom.head: 'head', Dom.header: 'header', Dom.hr: 'hr', Dom.html: 'html', Dom.i: 'i',
Dom.iframe: 'iframe', Dom.img: 'img', Dom.input: 'input', Dom.ins: 'ins', Dom.kbd: 'kbd', Dom.keygen: 'keygen',
Dom.label: 'label', Dom.legend: 'legend', Dom.li: 'li', Dom.link: 'link', Dom.main: 'main', Dom.map: 'map',
Dom.mark: 'mark', Dom.menu: 'menu', Dom.menuitem: 'menuitem', Dom.meta: 'meta', Dom.meter: 'meter',
Dom.nav: 'nav', Dom.noscript: 'noscript', Dom.object: 'object', Dom.ol: 'ol', Dom.optgroup: 'optgroup',
Dom.option: 'option', Dom.output: 'output', Dom.p: 'p', Dom.param: 'param', Dom.picture: 'picture',
Dom.pre: 'pre', Dom.progress: 'progress', Dom.q: 'q', Dom.rp: 'rp', Dom.rt: 'rt', Dom.ruby: 'ruby', Dom.s: 's',
Dom.samp: 'samp', Dom.script: 'script', Dom.section: 'section', Dom.select: 'select', Dom.small: 'small',
Dom.source: 'source', Dom.span: 'span', Dom.strong: 'strong', Dom.style: 'style', Dom.sub: 'sub',
Dom.summary: 'summary', Dom.sup: 'sup', Dom.table: 'table', Dom.tbody: 'tbody', Dom.td: 'td',
Dom.textarea: 'textarea', Dom.tfoot: 'tfoot', Dom.th: 'th', Dom.thead: 'thead', Dom.time: 'time',
Dom.title: 'title', Dom.tr: 'tr', Dom.track: 'track', Dom.u: 'u', Dom.ul: 'ul', Dom.variable: 'variable',
Dom.video: 'video', Dom.wbr: 'wbr',
// SVG
Dom.svgA: 'svgA', Dom.altGlyph: 'altGlyph', Dom.altGlyphDef: 'altGlyphDef', Dom.altGlyphItem: 'altGlyphItem',
Dom.animate: 'animate', Dom.animateColor: 'animateColor', Dom.animateMotion: 'animateMotion',
Dom.animateTransform: 'animateTransform', Dom.svgAudio: 'svgAudio', Dom.svgCanvas: 'svgCanvas', Dom.circle: 'circle',
Dom.clipPath: 'clipPath', Dom.colorProfile: 'colorProfile', Dom.cursor: 'cursor', Dom.defs: 'defs', Dom.desc: 'desc',
Dom.discard: 'discard', Dom.ellipse: 'ellipse', Dom.feBlend: 'feBlend', Dom.feColorMatrix: 'feColorMatrix',
Dom.feComponentTransfer: 'feComponentTransfer', Dom.feComposite: 'feComposite', Dom.feConvolveMatrix: 'feConvolveMatrix',
Dom.feDiffuseLighting: 'feDiffuseLighting', Dom.feDisplacementMap: 'feDisplacementMap',
Dom.feDistantLight: 'feDistantLight', Dom.feDropShadow: 'feDropShadow', Dom.feFlood: 'feFlood', Dom.feFuncA: 'feFuncA',
Dom.feFuncB: 'feFuncB', Dom.feFuncG: 'feFuncG', Dom.feFuncR: 'feFuncR', Dom.feGaussianBlur: 'feGaussianBlur',
Dom.feImage: 'feImage', Dom.feMerge: 'feMerge', Dom.feMergeNode: 'feMergeNode', Dom.feMorphology: 'feMorphology',
Dom.feOffset: 'feOffset', Dom.fePointLight: 'fePointLight', Dom.feSpecularLighting: 'feSpecularLighting',
Dom.feSpotLight: 'feSpotLight', Dom.feTile: 'feTile', Dom.feTurbulence: 'feTurbulence', Dom.filter: 'filter',
Dom.font: 'font', Dom.fontFace: 'fontFace', Dom.fontFaceFormat: 'fontFaceFormat', Dom.fontFaceName: 'fontFaceName',
Dom.fontFaceSrc: 'fontFaceSrc', Dom.fontFaceUri: 'fontFaceUri', Dom.foreignObject: 'foreignObject', Dom.g: 'g',
Dom.glyph: 'glyph', Dom.glyphRef: 'glyphRef', Dom.hatch: 'hatch', Dom.hatchpath: 'hatchpath', Dom.hkern: 'hkern',
Dom.svgIframe: 'svgIframe', Dom.image: 'image', Dom.line: 'line', Dom.linearGradient: 'linearGradient',
Dom.marker: 'marker', Dom.mask: 'mask', Dom.mesh: 'mesh', Dom.meshgradient: 'meshgradient', Dom.meshpatch: 'meshpatch',
Dom.meshrow: 'meshrow', Dom.metadata: 'metadata', Dom.missingGlyph: 'missingGlyph', Dom.mpath: 'mpath', Dom.path: 'path',
Dom.pattern: 'pattern', Dom.polygon: 'polygon', Dom.polyline: 'polyline', Dom.radialGradient: 'radialGradient',
Dom.rect: 'rect', Dom.svgScript: 'svgScript', Dom.svgSet: 'svgSet', Dom.solidcolor: 'solidcolor', Dom.stop: 'stop',
Dom.svgStyle: 'svgStyle', Dom.svg: 'svg', Dom.svgSwitch: 'svgSwitch', Dom.symbol: 'symbol', Dom.text: 'text',
Dom.textPath: 'textPath', Dom.svgTitle: 'svgTitle', Dom.tref: 'tref', Dom.tspan: 'tspan', Dom.unknown: 'unknown',
Dom.use: 'use', Dom.svgVideo: 'svgVideo', Dom.view: 'view', Dom.vkern: 'vkern',
};

expect(methods, isNotEmpty, reason: 'should have properly reflected the static members.');
} catch(e) {}

if (methods.isNotEmpty) {
expect(methods, unorderedEquals(expectedStaticMethods), reason: '`expectedStaticMethods` needs to be updated');
}
});

for (var element in expectedStaticMethods) {
String name = MirrorSystem.getName(element);
String expectedTagName = name;
expectedMethodsOnDom.forEach((method, methodName) {
String expectedTagName = methodName;
if (expectedTagName == 'variable') expectedTagName = 'var';
if (expectedTagName == 'svgSet') expectedTagName = 'set';
if (expectedTagName == 'svgSwitch') expectedTagName = 'switch';
Expand All @@ -83,12 +82,12 @@ main() {
if (expectedTagName == 'missingGlyph') expectedTagName = 'missing-glyph';
if (expectedTagName.startsWith(new RegExp('svg.'))) expectedTagName = expectedTagName.substring(3);

test('Dom.$name generates the correct type', () {
DomProps builder = domClassMirror.invoke(element, []).reflectee;
test('${method.toString()} generates the correct type', () {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using .toString() inside a string interpolation is redundant:

Suggested change
test('${method.toString()} generates the correct type', () {
test('$method generates the correct type', () {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤦‍♂️

DomProps builder = method();
ReactElement component = builder();
expect(component.type, equalsIgnoringCase(expectedTagName));
});
}
});

group('typing:', () {
final DomProps dom = Dom.div();
Expand Down
67 changes: 51 additions & 16 deletions test/over_react/component/prop_mixins_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,15 @@ import '../../test_util/prop_utils.dart';

main() {
group('ReactProps', () {
testKeys(ReactPropsMixin.meta.keys, (() => new ReactPropMixinsTest({})));
testInvalidKey((() => new ReactPropMixinsTest({})));
test('uses unnamespaced keys', () {
// Test two keys to reduce the possibility that the key we picked might have a custom @Accessor annotation on it
expect(
new ReactPropMixinsTest({})
..ref = null
..key = null,
{'ref': null, 'key': null});
});

group('prop: key can have its value set to', () {
test('an int and be read as a String', () {
Expand All @@ -50,19 +58,47 @@ main() {
});

group('CssClassProps', () {
testKeys(CssClassPropsMixin.meta.keys, (() => new CssClassPropMixinsTest({})));
testInvalidKey((() => new CssClassPropMixinsTest({})));
test('uses unnamespaced keys', () {
// Test two keys to reduce the possibility that the key we picked might have a custom @Accessor annotation on it
expect(new CssClassPropMixinsTest({})..className = null..classNameBlacklist = null, {'className': null, 'classNameBlacklist': null});
});
});

group('DomPropsMixin', () {
testKeys(DomPropsMixin.meta.keys, (() => new DomPropMixinsTest({})));
testInvalidKey((() => new DomPropMixinsTest({})));
test('uses unnamespaced keys', () {
// Test two keys to reduce the possibility that the key we picked might have a custom @Accessor annotation on it
expect(
new DomPropMixinsTest({})
..style = null
..id = null,
{'style': null, 'id': null});
});
});

group('SvgPropsMixin', () {
testKeys(SvgPropsMixin.meta.keys, (() => new SvgPropMixinsTest({})));
testInvalidKey((() => new SvgPropMixinsTest({})));
test('uses unnamespaced keys', () {
// Test two keys to reduce the possibility that the key we picked might have a custom @Accessor annotation on it
expect(
new SvgPropMixinsTest({})
..clipPath = null
..cx= null,
{'clipPath': null, 'cx': null});
});
});

group('UbiquitousProps', () {
testKeys(UbiquitousDomPropsMixin.meta.keys, (() => new UbiquitousPropMixinsTest({})));
testInvalidKey((() => new UbiquitousPropMixinsTest({})));
test('uses unnamespaced keys', () {
// Test two keys to reduce the possibility that the key we picked might have a custom @Accessor annotation on it
expect(
new UbiquitousPropMixinsTest({})
..tabIndex = null
..id = null,
{'tabIndex': null, 'id': null});
});

group('has a getter that provides a typed view of', () {
test('aria props', () {
Expand All @@ -86,17 +122,16 @@ main() {
});

group('AriaProps', () {
test('cannot set / read values that are not its prop map', () {
var instance = new AriaPropMixinsTest({});
expect(() {instance['notThere'];}, throwsArgumentError);
});

for (var propKey in AriaPropsMixin.meta.keys) {
test('prop: $propKey can have its value set / read', () {
var instance = new AriaPropMixinsTest({});
testProp(new Symbol(propKey.replaceFirst('aria-', '')), propKey, instance, null);
});
}
testInvalidKey(() => new AriaPropMixinsTest({}));

test('uses unnamespaced keys with \'aria-\' prefix', () {
// Test two keys to reduce the possibility that the key we picked might have a custom @Accessor annotation on it
expect(
new AriaPropMixinsTest({})
..activedescendant = null
..atomic = null,
{'aria-activedescendant': null, 'aria-atomic': null});
});
});
}

Expand Down
23 changes: 0 additions & 23 deletions test/test_util/mock_classes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,6 @@ library mock_classes;
import 'dart:async';
import 'dart:html';

// Tell dart2js that the `mockito` package only needs to reflect the specified mock/spied types.
// This speeds up compilation and makes JS output much smaller.
@MirrorsUsed(targets: const [
'dart.async.Timer',
'MockTimer',
// Also include Mock classes we use from w_test_tools.
'dart.dom.html.KeyEvent',
'dart.dom.html.HtmlDocument',
'w_test_tools.src.mock_classes.MockKeyEvent',
'w_test_tools.src.mock_classes.MockDocument',
'MockFileList',
'MockFile',
'MockFileUploadInputElement',
'dart.dom.html.FileList',
'dart.dom.html.File',
'dart.dom.html.FileUploadInputElement',
'MockSyntheticEvent',
'MockSyntheticMouseEvent',
'react.SyntheticEvent',
'react.SyntheticMouseEvent',
], override: 'mockito')
import 'dart:mirrors';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@greglittlefield-wf were these needed? afaik, the only usage of mirrors within mockito was spy(), which I didn't find any usages of in over_react's tests.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mm, looks like these were added under the false assumption that Mock classes used mirrors.

Away with them! 😄


import 'package:mockito/mockito.dart';
import 'package:react/react.dart' as react;

Expand Down
24 changes: 1 addition & 23 deletions test/test_util/prop_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,35 +14,13 @@

library test_util.prop_utils;

// Tell dart2js that this library only needs to reflect types annotated with `Props`/`PropsMixin`.
// This speeds up compilation and makes JS output much smaller.
@MirrorsUsed(metaTargets: const [
'over_react.component_declaration.annotations.Props',
'over_react.component_declaration.annotations.PropsMixin',
])
import 'dart:mirrors';

import 'package:over_react_test/over_react_test.dart';
import 'package:test/test.dart';

void testProp(Symbol name, dynamic expectedKey, instance, testValue) {
InstanceMirror mirror = reflect(instance);

mirror.setField(name, testValue);
expect(instance[expectedKey], equals(testValue));
expect(mirror.getField(name).reflectee, equals(testValue));
}

void testKeys(List<String> keys, dynamic instanceBuilder()) {
void testInvalidKey(dynamic instanceBuilder()) {
test('cannot set / read values that are not its prop map', () {
var instance = instanceBuilder();
expect(() {instance['notThere'];},
throwsA(hasToStringValue(contains('Map does not contain this key'))));
});
for (var propKey in keys) {
test('prop: $propKey can have its value set / read', () {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests were only intended to verify that the code generation for implementing concrete getters/setters was working properly, and that these props classes had a prop key namespace of '', (except for the case of aria props, which are prefixed with aria-). Therefore, these tests have been simplified here to just test on a few of the props on a mixin.

var instance = instanceBuilder();
testProp(new Symbol(propKey), propKey, instance, null);
});
}
}