Closed oliverlj closed 4 years ago
I am seeing the same thing in our tests. Looks like 3.17 was released yesterday. https://github.com/emberjs/ember.js/releases/tag/v3.17.0
In your case though, is the example wrong?
new Changeset(model
vs. new Changeset(this.model
?
all my app is in typescript, I don't think there is an impact with this keyword
Can you provide a bit more complete example? The PR description example seems like it is missing some pieces.
yes, sure, please find my component. //component utilisation
<RegistrationForm @onRegister={{action (perform register)}} />
//app/pods/components/registration-form/component.ts
import { action } from '@ember/object';
import { inject as service } from '@ember/service';
import Component from '@glimmer/component';
import Changeset from 'ember-changeset';
import lookupValidator from 'ember-changeset-validations';
import { BufferedChangeset } from 'ember-changeset/types';
import StoreService from 'ember-data/store';
import UserValidations from './register-user-validations';
interface RegistrationFormArgs {
onRegister: (changeset: BufferedChangeset) => void;
}
export default class RegistrationForm extends Component<RegistrationFormArgs> {
@service store!: StoreService;
model = this.store.createRecord('user');
changeset: BufferedChangeset = Changeset(this.model, lookupValidator(UserValidations), UserValidations);
get isRegisterButtonDisabled() {
return (
this.changeset.isInvalid ||
!this.changeset.change.username ||
!this.changeset.change.password ||
!this.changeset.change.passwordConfirmation
);
}
@action
erasePasswordConfirmation() {
if (this.changeset.change.passwordConfirmation) {
this.changeset.set('passwordConfirmation', '');
}
}
}
//app/pods/components/registration-form/template.hbs
<form>
<div class="input-group mb-3">
<Input id="registration-form-input-username"
class="form-control {{if changeset.error.username "is-invalid"}} {{if (and (not changeset.error.username) (not (is-empty changeset.change.username))) "is-valid"}}"
placeholder="Nom d'utilisateur *" @value={{changeset.username}} />
<div class="input-group-append">
<div class="input-group-text">
<FaIcon @icon="user" />
</div>
</div>
{{#if changeset.error.username}}
<div class="invalid-feedback">
{{t (concat "register." changeset.error.username.validation ".username")}}
</div>
{{/if}}
</div>
<div class="input-group mb-3">
<Input type="email" class="form-control {{if changeset.error.email "is-invalid"}}" placeholder="Email"
@value={{changeset.email}} />
<div class="input-group-append">
<div class="input-group-text">
<FaIcon @icon="envelope" />
</div>
</div>
<div class="invalid-feedback">
Veuillez saisir un email valide
</div>
</div>
<div class="input-group mb-3">
<Input type="password" id="registration-form-input-password"
class="form-control {{if changeset.error.password "is-invalid"}} {{if (and (not changeset.error.password) (not (is-empty changeset.change.password))) "is-valid"}}"
placeholder="Mot de passe *" @key-up={{action "erasePasswordConfirmation"}} @value={{changeset.password}} />
<div class="input-group-append">
<div class="input-group-text">
<FaIcon @icon="lock" />
</div>
</div>
<div class="invalid-feedback">
Le mot de passe doit être au moins de 8 charactères
</div>
</div>
<div class="input-group mb-3">
<Input type="password" id="registration-form-input-passwordConfirmation"
class="form-control {{if changeset.error.passwordConfirmation "is-invalid"}} {{if (and (not changeset.error.passwordConfirmation) (not (is-empty changeset.change.passwordConfirmation))) "is-valid"}}"
placeholder="Retaper votre mot de passe *" @value={{changeset.passwordConfirmation}} />
<div class=" input-group-append">
<div class="input-group-text">
<FaIcon @icon="lock" />
</div>
</div>
<div class="invalid-feedback">
Le mot de passe doit être identique.
</div>
</div>
<div class="row">
<div class="col-7">
<div class="icheck-primary" data-toggle="tooltip" title={{t "application.not-yet-implemented"}}>
<input type="checkbox" id="agreeTerms" name="terms" value="agree" disabled>
<label for="agreeTerms">
J'accepte
<a class="btn-link disabled" href="#" aria-disabled="true">les conditions </a>
</label>
</div>
</div>
<div class="col-5">
<button id="registration-form-submit" type="submit"
class="btn btn-primary btn-block btn-flat {{if isRegisterButtonDisabled "disabled"}}"
disabled={{this.isRegisterButtonDisabled}} {{action @onRegister changeset}}>
S'inscrire
</button>
</div>
</div>
</form>
//app/pods/components/registration-form/register-user-validations.ts
import { validateConfirmation, validateFormat } from 'ember-changeset-validations/validators';
import LoginUserValidations from 'fritzy-front/validations/login-user';
export const RegisterUserValidations = {
email: validateFormat({ allowBlank: true, type: 'email' }),
passwordConfirmation: validateConfirmation({ on: 'password' })
};
export default Object.assign({}, LoginUserValidations, RegisterUserValidations);
//tests/integration/pods/components/registration-form/component-test.ts
import { click, fillIn, render } from '@ember/test-helpers';
import { BufferedChangeset } from 'ember-changeset/types';
import { hbs } from 'ember-cli-htmlbars';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import { setupIntl } from 'ember-intl/test-support';
import { setupRenderingTest } from 'ember-qunit';
import User from 'fritzy-front/models/user';
import { module, test } from 'qunit';
import sinon from 'sinon';
import { isChangeset } from 'validated-changeset';
module('Integration | Component | registration-form', hooks => {
setupRenderingTest(hooks);
setupIntl(hooks);
setupMirage(hooks);
test('it renders', async function(assert) {
// Given
this.set('registerAction', sinon.stub());
// When
await render(hbs`<RegistrationForm @onRegister={{action registerAction}} />`);
// Then
assert.dom('#registration-form-submit').exists('Registration form submit button not found!');
});
test('it register user', async function(assert) {
// Given
const registerAction = sinon.spy();
this.set('registerAction', registerAction);
await render(hbs`<RegistrationForm @onRegister={{action registerAction}} />`);
await fillIn('#registration-form-input-username', 'oliver');
await fillIn('#registration-form-input-password', 'password');
await fillIn('#registration-form-input-passwordConfirmation', 'password');
// When
await click('#registration-form-submit');
// Then
assert.ok(registerAction.called, 'Register action shoud be called');
assert.ok(isChangeset(registerAction.getCalls()[0].args[0]), 'A changeset should be pass at the register action');
});
test('it show error on username already taken', async function(assert) {
// Given
server.post(
'/users',
{
errors: [
{
id: null,
links: null,
status: '422',
code: null,
title: 'key:error.back.user.not-unique-username',
detail: null,
source: { pointer: 'data/attributes/username', parameter: null },
meta: null
}
]
},
422
);
function registerAction(changeset: BufferedChangeset) {
changeset.save().catch(() =>
(changeset.data as User).get('errors').forEach(({ attribute, message }) => {
changeset.pushErrors(attribute, message);
})
);
}
this.set('registerAction', registerAction);
await render(hbs`<RegistrationForm @onRegister={{action registerAction}} />`);
await fillIn('#registration-form-input-username', 'oliver');
await fillIn('#registration-form-input-password', 'password');
await fillIn('#registration-form-input-passwordConfirmation', 'password');
// When
await click('#registration-form-submit');
// Then
assert
.dom('#registration-form-input-username.is-invalid')
.exists('Registration form input username should be invalid');
});
});
all test are in error, since the error happen on component initialization
What happens when you throw the following two lines in the constructor? Also I thought we need this.changeset
in the template since it is a glimmer component.
constructor() {
super(...arguments);
model = this.store.createRecord('user');
changeset: BufferedChangeset = Changeset(this.model, lookupValidator(UserValidations), UserValidations);
}
Without the new :
Uncaught TypeError: Class constructor ChangesetKlass cannot be invoked without 'new'
at new RegistrationForm (component.ts:24)
at EmberGlimmerComponentManager.createComponent (base-component-manager.js:39)
at CustomComponentManager.create (index.js:5471)
at Object.evaluate (runtime.js:3394)
at AppendOpcodes.evaluate (runtime.js:2030)
at LowLevelVM.evaluateSyscall (runtime.js:4932)
at LowLevelVM.evaluateInner (runtime.js:4888)
at LowLevelVM.evaluateOuter (runtime.js:4880)
at JitVM.next (runtime.js:5823)
at JitVM.execute (runtime.js:5807)
ok, so now with this.changeset in the template. it is working.
I have a Typescript error with new Changeset :
This expression is not constructable.
Type 'typeof import("/home/oliver/git/fritzy-front/node_modules/ember-changeset/index")' has no construct signatures.ts(2351)
Perfect. And with the Changeset
function you need to import it like { Changeset }
.
Let me know if you have other issues!
Thanks for your time ! :) Maybe BufferedChangeset could be generic ? With this, this.changeset.data will return the good type
Hello guys,
I have this component :
My integrations test are failed with an update to ember 3.17 with this error :