-
Notifications
You must be signed in to change notification settings - Fork 41
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
Auto bind component data-test-* attributes #27
Changes from all commits
50257c3
af385c6
0d31bc0
3e69cc5
151aad0
b75c9f4
7868eda
f48e6ad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
module.exports = { | ||
parserOptions: { | ||
ecmaVersion: 6, | ||
sourceType: 'module', | ||
}, | ||
env: { | ||
node: true | ||
}, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import Ember from 'ember'; | ||
import bindDataTestAttributes from '../utils/bind-data-test-attributes'; | ||
|
||
function initialize(/* application */) { | ||
Ember.Component.reopen({ | ||
init() { | ||
this._super(...arguments); | ||
bindDataTestAttributes(this); | ||
} | ||
}); | ||
} | ||
|
||
export default { | ||
name: 'ember-test-selectors', | ||
initialize | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import Ember from 'ember'; | ||
|
||
const TEST_SELECTOR_PREFIX = /data-test-.*/; | ||
|
||
export default function bindDataTestAttributes(component) { | ||
let computedBindings = component.attributeBindings && component.attributeBindings.isDescriptor; | ||
if (computedBindings) { | ||
let message = `ember-test-selectors could not bind data-test-* properties on ${component} ` + | ||
`automatically because attributeBindings is a computed property.`; | ||
|
||
return Ember.warn(message, false, { | ||
id: 'ember-test-selectors.computed-attribute-bindings', | ||
}); | ||
} | ||
|
||
let attributeBindings = component.getWithDefault('attributeBindings', []); | ||
|
||
if (!Ember.isArray(attributeBindings)) { | ||
attributeBindings = [attributeBindings]; | ||
} | ||
|
||
for (let attr in component) { | ||
if (TEST_SELECTOR_PREFIX.test(attr)) { | ||
attributeBindings.push(attr); | ||
} | ||
} | ||
|
||
component.set('attributeBindings', attributeBindings); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess this doesn't work though if the original There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that is true, but I'm not sure if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah good point. Could you check this though?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm now checking if |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
module.exports = { | ||
parserOptions: { | ||
ecmaVersion: 6, | ||
sourceType: 'module', | ||
}, | ||
env: { | ||
node: true | ||
}, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { default } from 'ember-test-selectors/initializers/ember-test-selectors'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { test } from 'qunit'; | ||
import moduleForAcceptance from '../../tests/helpers/module-for-acceptance'; | ||
|
||
import config from 'dummy/config/environment'; | ||
|
||
if (!config.stripTestSelectors) { | ||
|
||
/* | ||
* We use an acceptance test here to test the "ember-test-selectors" initializer, | ||
* because initializers are only applied in acceptance tests, but not in | ||
* component integration tests. | ||
*/ | ||
moduleForAcceptance('Acceptance | Initializer | ember-test-selectors', { | ||
beforeEach() { | ||
visit('/bind-test'); | ||
}, | ||
}); | ||
|
||
test('it binds data-test-* attributes on components', function (assert) { | ||
assert.equal(find('.test1').find('div[data-test-first]').length, 1, 'data-test-first exists'); | ||
assert.equal(find('.test1').find('div[data-test-first="foobar"]').length, 1, 'data-test-first has correct value'); | ||
}); | ||
|
||
test('it binds data-test-* attributes on components in block form', function (assert) { | ||
assert.equal(find('.test2').find('div[data-test-first]').length, 1, 'data-test-first exists'); | ||
assert.equal(find('.test2').find('div[data-test-first="foobar"]').length, 1, 'data-test-first has correct value'); | ||
}); | ||
|
||
test('it works with multiple data-test-* attributes on components', function (assert) { | ||
assert.equal(find('.test3').find('div[data-test-first]').length, 1, 'data-test-first exists'); | ||
assert.equal(find('.test3').find('div[data-test-first="foobar"]').length, 1, 'data-test-first has correct value'); | ||
assert.equal(find('.test3').find('div[data-test-second]').length, 1, 'data-test-second exists'); | ||
assert.equal(find('.test3').find('div[data-test-second="second"]').length, 1, 'data-test-second has correct value'); | ||
}); | ||
|
||
test('it leaves other data attributes untouched, when a data-test-* attribute is present as well on components', function (assert) { | ||
assert.equal(find('.test4').find('div[data-test-first]').length, 1, 'data-test-first exists'); | ||
assert.equal(find('.test4').find('div[data-test-first="foobar"]').length, 1, 'data-test-first has correct value'); | ||
assert.equal(find('.test4').find('div[data-non-test]').length, 0, 'data-non-test does not exists'); | ||
}); | ||
|
||
test('it leaves data-test attributes untouched on components', function (assert) { | ||
assert.equal(find('.test5').find('div[data-test]').length, 0, 'data-test does not exists'); | ||
}); | ||
|
||
test('it leaves other data attributes untouched on components', function (assert) { | ||
assert.equal(find('.test6').find('div[data-non-test]').length, 0, 'data-non-test does not exists'); | ||
}); | ||
|
||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should have a test for the case where the component defines There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @marcoow we test that in the unit tests for the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, just assumed both might actually be supported. If we have a unit test for this already that should be fine. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since attributeBindings: "myAttribute" which will become
and will not work for: attributeBindings: "myAttribute myOtherAttribute" which would result in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. http://emberjs.com/api/classes/Ember.View.html#toc_html-attributes only uses arrays, so I'm assuming passing a string is not officially supported. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since it's not official API, I would tend to agree with @Turbo87 and not support this tbh. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've implemented a quick array wrapper now, see f48e6ad |
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<div class="test1">{{data-test-component data-test-first="foobar"}}</div> | ||
|
||
<div class="test2">{{#data-test-component data-test-first="foobar"}}hello{{/data-test-component}}</div> | ||
|
||
<div class="test3">{{data-test-component data-test-first="foobar" data-test-second="second"}}</div> | ||
|
||
<div class="test4">{{data-test-component data-test-first="foobar" data-non-test="baz"}}</div> | ||
|
||
<div class="test5">{{data-test-component data-test="foo"}}</div> | ||
|
||
<div class="test6">{{data-test-component data-non-test="foo"}}</div> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
import { module, test } from 'qunit'; | ||
import Ember from 'ember'; | ||
|
||
import bindDataTestAttributes from 'ember-test-selectors/utils/bind-data-test-attributes'; | ||
|
||
module('Unit | Utility | bind data test attributes'); | ||
|
||
test('it adds missing attributeBindings array', function(assert) { | ||
let Fixture = Ember.Object.extend({ | ||
'data-test-from-factory': 'foo', | ||
}); | ||
let instance = Fixture.create({ | ||
'data-test-from-invocation': 'bar', | ||
}); | ||
|
||
assert.deepEqual(instance.get('attributeBindings'), undefined); | ||
|
||
bindDataTestAttributes(instance); | ||
|
||
assert.deepEqual(instance.get('attributeBindings'), | ||
['data-test-from-invocation', 'data-test-from-factory']); | ||
}); | ||
|
||
test('it adds to existing attributeBindings array', function(assert) { | ||
let Fixture = Ember.Object.extend({ | ||
attributeBindings: ['foo', 'bar'], | ||
|
||
foo: 1, | ||
bar: 2, | ||
|
||
'data-test-from-factory': 'foo', | ||
}); | ||
let instance = Fixture.create({ | ||
'data-test-from-invocation': 'bar', | ||
}); | ||
|
||
assert.deepEqual(instance.get('attributeBindings'), ['foo', 'bar']); | ||
|
||
bindDataTestAttributes(instance); | ||
|
||
assert.deepEqual(instance.get('attributeBindings'), | ||
['foo', 'bar', 'data-test-from-invocation', 'data-test-from-factory']); | ||
}); | ||
|
||
test('it converts existing attributeBindings string to array', function(assert) { | ||
let Fixture = Ember.Object.extend({ | ||
attributeBindings: 'foo', | ||
|
||
foo: 1, | ||
|
||
'data-test-from-factory': 'foo', | ||
}); | ||
let instance = Fixture.create({ | ||
'data-test-from-invocation': 'bar', | ||
}); | ||
|
||
assert.deepEqual(instance.get('attributeBindings'), 'foo'); | ||
|
||
bindDataTestAttributes(instance); | ||
|
||
assert.deepEqual(instance.get('attributeBindings'), | ||
['foo', 'data-test-from-invocation', 'data-test-from-factory']); | ||
}); | ||
|
||
test('it only adds data-test-* properties', function(assert) { | ||
let Fixture = Ember.Object.extend({ | ||
foo: 1, | ||
bar: 2, | ||
|
||
'data-test-from-factory': 'foo', | ||
}); | ||
let instance = Fixture.create({ | ||
baz: 3, | ||
|
||
'data-test-from-invocation': 'bar', | ||
}); | ||
|
||
assert.deepEqual(instance.get('attributeBindings'), undefined); | ||
|
||
bindDataTestAttributes(instance); | ||
|
||
assert.deepEqual(instance.get('attributeBindings'), | ||
['data-test-from-invocation', 'data-test-from-factory']); | ||
}); | ||
|
||
test('it does not add a data-test property', function(assert) { | ||
let Fixture = Ember.Object.extend({ | ||
'data-test': 'foo', | ||
}); | ||
let instance = Fixture.create(); | ||
|
||
assert.deepEqual(instance.get('attributeBindings'), undefined); | ||
|
||
bindDataTestAttributes(instance); | ||
|
||
assert.deepEqual(instance.get('attributeBindings'), []); | ||
}); | ||
|
||
test('it skips if attributeBindings is a computed property', function(assert) { | ||
let Fixture = Ember.Object.extend({ | ||
attributeBindings: Ember.computed('prop', function() { | ||
return [this.get('prop')]; | ||
}).readOnly(), | ||
|
||
foo: 5, | ||
|
||
'data-test-from-factory': 'foo', | ||
}); | ||
let instance = Fixture.create({ | ||
prop: 'foo', | ||
|
||
'data-test-from-invocation': 'bar', | ||
}); | ||
|
||
assert.deepEqual(instance.get('attributeBindings'), ['foo']); | ||
|
||
bindDataTestAttributes(instance); | ||
|
||
assert.deepEqual(instance.get('attributeBindings'), ['foo']); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we could somehow make it work correctly in this case as well that would be awesome of course. In theory it should be possible to replace the CP with a wrapper one that we create of course but I'm not sure it's worth the added complexity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure it's worth the extra complexity to be honest. I've never seen anyone use a CP for
attributeBindings
and I have quite a hard time coming up with use cases where that would be a good idea. I'd suggest keeping the warning for now, and if many users report that they are seeing this warning then we can still think about implementing it.