Fixes #3137
- Modernize the `RegisterPanel` component and turn it into a Lit element. - Improve CSS and move into plugin. - Fix button click handler not being registered. - Fix switching between login/register form after logging out (Fixes #1556)
This commit is contained in:
parent
8035084e8e
commit
7b8b32638c
|
@ -1,5 +1,10 @@
|
|||
# Changelog
|
||||
|
||||
## 10.1.2 (Unreleased)
|
||||
|
||||
- #1556: Can't switch to registration form afrer logout
|
||||
- #3137: Various UI/UX bugfixes regarding the registration form
|
||||
|
||||
## 10.1.1 (2023-02-15)
|
||||
|
||||
- #1851: Sort open groupchats alphabetically
|
||||
|
|
|
@ -60,7 +60,7 @@ converse.plugins.add('converse-controlbox', {
|
|||
sticky_controlbox: false
|
||||
});
|
||||
|
||||
api.promises.add('controlBoxInitialized');
|
||||
api.promises.add('controlBoxInitialized', false);
|
||||
Object.assign(api, controlbox_api);
|
||||
|
||||
_converse.ControlBoxView = ControlBoxView;
|
||||
|
|
|
@ -109,56 +109,6 @@
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
#converse-register {
|
||||
@include fade-in;
|
||||
background-color: var(--controlbox-pane-background-color);
|
||||
.title {
|
||||
font-weight: bold;
|
||||
}
|
||||
.info {
|
||||
color: green;
|
||||
font-size: 90%;
|
||||
margin: 1.5em 0;
|
||||
}
|
||||
.form-errors {
|
||||
color: var(--error-color);
|
||||
margin: 1em 0;
|
||||
}
|
||||
.provider-title {
|
||||
font-size: var(--font-size-huge);
|
||||
margin: 0;
|
||||
}
|
||||
.provider-score {
|
||||
width: 178px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.form-help .url {
|
||||
font-weight: bold;
|
||||
color: var(--link-color);
|
||||
}
|
||||
.input-group {
|
||||
display: table;
|
||||
margin: auto;
|
||||
width: 100%;
|
||||
span {
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
max-width: 110px;
|
||||
}
|
||||
span, input[name=username] {
|
||||
display: table-cell;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
.instructions {
|
||||
color: gray;
|
||||
font-size: 85%;
|
||||
&:hover {
|
||||
color: var(--controlbox-text-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.conn-feedback {
|
||||
color: var(--controlbox-head-color);
|
||||
&.error {
|
||||
|
|
|
@ -60,7 +60,7 @@ const password_input = () => {
|
|||
`;
|
||||
}
|
||||
|
||||
const register_link = () => {
|
||||
const tplRegisterLink = () => {
|
||||
const i18n_create_account = __("Create an account");
|
||||
const i18n_hint_no_account = __("Don't have a chat account?");
|
||||
return html`
|
||||
|
@ -71,7 +71,7 @@ const register_link = () => {
|
|||
`;
|
||||
}
|
||||
|
||||
const show_register_link = () => {
|
||||
const tplShowRegisterLink = () => {
|
||||
return api.settings.get('allow_registration') &&
|
||||
!api.settings.get("auto_login") &&
|
||||
_converse.pluggable.plugins['converse-register'].enabled(_converse);
|
||||
|
@ -106,7 +106,7 @@ const auth_fields = (el) => {
|
|||
<fieldset class="form-group buttons">
|
||||
<input class="btn btn-primary" type="submit" value="${i18n_login}"/>
|
||||
</fieldset>
|
||||
${ show_register_link() ? register_link() : '' }
|
||||
${ tplShowRegisterLink() ? tplRegisterLink(el) : '' }
|
||||
`;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
import './panel.js';
|
||||
import { __ } from 'i18n';
|
||||
import { _converse, api, converse } from '@converse/headless/core';
|
||||
import { setActiveForm } from './utils.js';
|
||||
|
||||
// Strophe methods for building stanzas
|
||||
const { Strophe } = converse.env;
|
||||
|
@ -32,6 +33,8 @@ converse.plugins.add('converse-register', {
|
|||
},
|
||||
|
||||
initialize () {
|
||||
const { router } = _converse;
|
||||
|
||||
_converse.CONNECTION_STATUS[Strophe.Status.REGIFAIL] = 'REGIFAIL';
|
||||
_converse.CONNECTION_STATUS[Strophe.Status.REGISTERED] = 'REGISTERED';
|
||||
_converse.CONNECTION_STATUS[Strophe.Status.CONFLICT] = 'CONFLICT';
|
||||
|
@ -44,17 +47,7 @@ converse.plugins.add('converse-register', {
|
|||
'registration_domain': ''
|
||||
});
|
||||
|
||||
async function setActiveForm (value) {
|
||||
await api.waitUntil('controlBoxInitialized');
|
||||
const controlbox = _converse.chatboxes.get('controlbox');
|
||||
controlbox.set({ 'active-form': value });
|
||||
}
|
||||
_converse.router.route('converse/login', () => setActiveForm('login'));
|
||||
_converse.router.route('converse/register', () => setActiveForm('register'));
|
||||
|
||||
|
||||
api.listen.on('controlBoxInitialized', view => {
|
||||
view.model.on('change:active-form', view.showLoginOrRegisterForm, view);
|
||||
});
|
||||
router.route('converse/login', () => setActiveForm('login'));
|
||||
router.route('converse/register', () => setActiveForm('register'));
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
import log from "@converse/headless/log";
|
||||
import pick from "lodash-es/pick";
|
||||
import tplFormInput from "templates/form_input.js";
|
||||
import tplFormUrl from "templates/form_url.js";
|
||||
import tplFormUsername from "templates/form_username.js";
|
||||
import tplRegisterPanel from "./templates/register_panel.js";
|
||||
import tplSpinner from "templates/spinner.js";
|
||||
import { webForm2xForm } from "@converse/headless/utils/form";
|
||||
import { ElementView } from "@converse/skeletor/src/element";
|
||||
import { CustomElement } from 'shared/components/element.js';
|
||||
import { __ } from 'i18n';
|
||||
import { _converse, api, converse } from "@converse/headless/core.js";
|
||||
import { initConnection } from '@converse/headless/utils/init.js';
|
||||
import { render } from 'lit';
|
||||
import { setActiveForm } from './utils.js';
|
||||
import { webForm2xForm } from "@converse/headless/utils/form";
|
||||
|
||||
import './styles/register.scss';
|
||||
|
||||
// Strophe methods for building stanzas
|
||||
const { Strophe, sizzle, $iq } = converse.env;
|
||||
|
@ -27,38 +29,29 @@ const REGISTRATION_FORM = 2;
|
|||
* @namespace _converse.RegisterPanel
|
||||
* @memberOf _converse
|
||||
*/
|
||||
class RegisterPanel extends ElementView {
|
||||
id = "converse-register-panel"
|
||||
className = 'controlbox-pane fade-in'
|
||||
events = {
|
||||
'submit form#converse-register': 'onFormSubmission',
|
||||
'click .button-cancel': 'renderProviderChoiceForm',
|
||||
class RegisterPanel extends CustomElement {
|
||||
|
||||
static get properties () {
|
||||
return {
|
||||
status : { type: String },
|
||||
error_message: { type: String },
|
||||
}
|
||||
}
|
||||
|
||||
initialize () {
|
||||
this.reset();
|
||||
const controlbox = _converse.chatboxes.get('controlbox');
|
||||
this.model = controlbox;
|
||||
this.listenTo(_converse, 'connectionInitialized', this.registerHooks);
|
||||
this.listenTo(this.model, 'change:registration_status', this.render);
|
||||
this.listenTo(_converse, 'connectionInitialized', () => this.registerHooks());
|
||||
|
||||
const domain = api.settings.get('registration_domain');
|
||||
if (domain) {
|
||||
this.fetchRegistrationForm(domain);
|
||||
} else {
|
||||
this.model.set('registration_status', CHOOSE_PROVIDER);
|
||||
this.status = CHOOSE_PROVIDER;
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
render(tplRegisterPanel({
|
||||
'domain': this.domain,
|
||||
'fields': this.fields,
|
||||
'form_fields': this.form_fields,
|
||||
'instructions': this.instructions,
|
||||
'model': this.model,
|
||||
'title': this.title,
|
||||
}), this);
|
||||
return tplRegisterPanel(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -79,11 +72,6 @@ class RegisterPanel extends ElementView {
|
|||
};
|
||||
}
|
||||
|
||||
connectedCallback () {
|
||||
super.connectedCallback();
|
||||
this.render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an IQ stanza to the XMPP server asking for the registration fields.
|
||||
* @private
|
||||
|
@ -98,9 +86,8 @@ class RegisterPanel extends ElementView {
|
|||
const body = conn._proto._reqToData(req);
|
||||
if (!body) { return; }
|
||||
if (conn._proto._connect_cb(body) === Strophe.Status.CONNFAIL) {
|
||||
this.showValidationError(
|
||||
__("Sorry, we're unable to connect to your chosen provider.")
|
||||
);
|
||||
this.status = CHOOSE_PROVIDER;
|
||||
this.error_message = __("Sorry, we're unable to connect to your chosen provider.");
|
||||
return false;
|
||||
}
|
||||
const register = body.getElementsByTagName("register");
|
||||
|
@ -111,14 +98,14 @@ class RegisterPanel extends ElementView {
|
|||
}
|
||||
if (register.length === 0) {
|
||||
conn._changeConnectStatus(Strophe.Status.REGIFAIL);
|
||||
this.showValidationError(
|
||||
this.error_message =
|
||||
__("Sorry, the given provider does not support in "+
|
||||
"band account registration. Please try with a "+
|
||||
"different provider."))
|
||||
"different provider.");
|
||||
return true;
|
||||
}
|
||||
// Send an IQ stanza to get all required data fields
|
||||
conn._addSysHandler(this.onRegistrationFields.bind(this), null, "iq", null, null);
|
||||
conn._addSysHandler((s) => this.onRegistrationFields(s), null, "iq", null, null);
|
||||
const stanza = $iq({type: "get"}).c("query", {xmlns: Strophe.NS.REGISTER}).tree();
|
||||
stanza.setAttribute("id", conn.getUniqueId("sendIQ"));
|
||||
conn.send(stanza);
|
||||
|
@ -149,7 +136,7 @@ class RegisterPanel extends ElementView {
|
|||
return false;
|
||||
}
|
||||
this.setFields(stanza);
|
||||
if (this.model.get('registration_status') === FETCHING_FORM) {
|
||||
if (this.status === FETCHING_FORM) {
|
||||
this.renderRegistrationForm(stanza);
|
||||
}
|
||||
return false;
|
||||
|
@ -167,9 +154,7 @@ class RegisterPanel extends ElementView {
|
|||
form_type: null
|
||||
};
|
||||
Object.assign(this, defaults);
|
||||
if (settings) {
|
||||
Object.assign(this, pick(settings, Object.keys(defaults)));
|
||||
}
|
||||
if (settings) Object.assign(this, settings);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -179,7 +164,7 @@ class RegisterPanel extends ElementView {
|
|||
* @param { Event } ev
|
||||
*/
|
||||
onFormSubmission (ev) {
|
||||
if (ev && ev.preventDefault) { ev.preventDefault(); }
|
||||
ev?.preventDefault?.();
|
||||
if (ev.target.querySelector('input[name=domain]') === null) {
|
||||
this.submitRegistrationForm(ev.target);
|
||||
} else {
|
||||
|
@ -195,15 +180,8 @@ class RegisterPanel extends ElementView {
|
|||
* @param { HTMLElement } form - The form that was submitted
|
||||
*/
|
||||
onProviderChosen (form) {
|
||||
const domain_input = form.querySelector('input[name=domain]'),
|
||||
domain = domain_input?.value;
|
||||
if (!domain) {
|
||||
// TODO: add validation message
|
||||
domain_input.classList.add('error');
|
||||
return;
|
||||
}
|
||||
form.querySelector('input[type=submit]').classList.add('hidden');
|
||||
this.fetchRegistrationForm(domain.trim());
|
||||
const domain = form.querySelector('input[name=domain]')?.value;
|
||||
if (domain) this.fetchRegistrationForm(domain.trim());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -213,7 +191,7 @@ class RegisterPanel extends ElementView {
|
|||
* @param { String } domain_name - XMPP server domain
|
||||
*/
|
||||
fetchRegistrationForm (domain_name) {
|
||||
this.model.set('registration_status', FETCHING_FORM);
|
||||
this.status = FETCHING_FORM;
|
||||
this.reset({
|
||||
'domain': Strophe.getDomainFromJid(domain_name),
|
||||
'_registering': true
|
||||
|
@ -221,7 +199,7 @@ class RegisterPanel extends ElementView {
|
|||
initConnection(this.domain);
|
||||
// When testing, the test tears down before the async function
|
||||
// above finishes. So we use optional chaining here
|
||||
_converse.connection?.connect(this.domain, "", status => this.onConnectStatusChanged(status));
|
||||
_converse.connection?.connect(this.domain, "", (s) => this.onConnectStatusChanged(s));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -281,6 +259,8 @@ class RegisterPanel extends ElementView {
|
|||
this.fields.password,
|
||||
_converse.onConnectStatusChanged
|
||||
);
|
||||
setActiveForm('login');
|
||||
_converse.router.navigate('');
|
||||
this.giveFeedback(__('Now logging you in'), 'info');
|
||||
} else {
|
||||
_converse.giveFeedback(__('Registered successfully'));
|
||||
|
@ -334,29 +314,7 @@ class RegisterPanel extends ElementView {
|
|||
*/
|
||||
renderRegistrationForm (stanza) {
|
||||
this.form_fields = this.getFormFields(stanza);
|
||||
this.model.set('registration_status', REGISTRATION_FORM);
|
||||
}
|
||||
|
||||
showValidationError (message) {
|
||||
const form = this.querySelector('form');
|
||||
let flash = form.querySelector('.form-errors');
|
||||
if (flash === null) {
|
||||
flash = '<div class="form-errors hidden"></div>';
|
||||
const instructions = form.querySelector('p.instructions');
|
||||
if (instructions === null) {
|
||||
form.insertAdjacentHTML('afterbegin', flash);
|
||||
} else {
|
||||
instructions.insertAdjacentHTML('afterend', flash);
|
||||
}
|
||||
flash = form.querySelector('.form-errors');
|
||||
} else {
|
||||
flash.innerHTML = '';
|
||||
}
|
||||
flash.insertAdjacentHTML(
|
||||
'beforeend',
|
||||
'<p class="form-help error">'+message+'</p>'
|
||||
);
|
||||
flash.classList.remove('hidden');
|
||||
this.status = REGISTRATION_FORM;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -367,31 +325,31 @@ class RegisterPanel extends ElementView {
|
|||
* @param { XMLElement } stanza - The IQ stanza received from the XMPP server
|
||||
*/
|
||||
reportErrors (stanza) {
|
||||
const errors = stanza.querySelectorAll('error');
|
||||
errors.forEach(e => this.showValidationError(e.textContent));
|
||||
if (!errors.length) {
|
||||
const message = __('The provider rejected your registration attempt. '+
|
||||
const errors = Array.from(stanza.querySelectorAll('error'));
|
||||
if (errors.length) {
|
||||
this.error_message = errors.reduce((result, e) => `${result}\n${e.textContent}`, '');
|
||||
} else {
|
||||
this.error_message = __('The provider rejected your registration attempt. '+
|
||||
'Please check the values you entered for correctness.');
|
||||
this.showValidationError(message);
|
||||
}
|
||||
}
|
||||
|
||||
renderProviderChoiceForm (ev) {
|
||||
if (ev && ev.preventDefault) { ev.preventDefault(); }
|
||||
ev?.preventDefault?.();
|
||||
_converse.connection._proto._abortAllRequests();
|
||||
_converse.connection.reset();
|
||||
this.render();
|
||||
this.status = CHOOSE_PROVIDER;
|
||||
}
|
||||
|
||||
abortRegistration () {
|
||||
_converse.connection._proto._abortAllRequests();
|
||||
_converse.connection.reset();
|
||||
if ([FETCHING_FORM, REGISTRATION_FORM].includes(this.model.get('registration_status'))) {
|
||||
if ([FETCHING_FORM, REGISTRATION_FORM].includes(this.status)) {
|
||||
if (api.settings.get('registration_domain')) {
|
||||
this.fetchRegistrationForm(api.settings.get('registration_domain'));
|
||||
}
|
||||
} else {
|
||||
this.render();
|
||||
this.requestUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -403,16 +361,6 @@ class RegisterPanel extends ElementView {
|
|||
* @param { HTMLElement } form - The HTML form that was submitted
|
||||
*/
|
||||
submitRegistrationForm (form) {
|
||||
const has_empty_inputs = Array.from(this.querySelectorAll('input.required'))
|
||||
.reduce((result, input) => {
|
||||
if (input.value === '') {
|
||||
input.classList.add('error');
|
||||
return result + 1;
|
||||
}
|
||||
return result;
|
||||
}, 0);
|
||||
if (has_empty_inputs) { return; }
|
||||
|
||||
const inputs = sizzle(':input:not([type=button]):not([type=submit])', form);
|
||||
const iq = $iq({'type': 'set', 'id': u.getUniqueId()})
|
||||
.c("query", {xmlns:Strophe.NS.REGISTER});
|
||||
|
@ -425,7 +373,7 @@ class RegisterPanel extends ElementView {
|
|||
} else {
|
||||
inputs.forEach(input => iq.c(input.getAttribute('name'), {}, input.value));
|
||||
}
|
||||
_converse.connection._addSysHandler(this._onRegisterIQ.bind(this), null, "iq", null, null);
|
||||
_converse.connection._addSysHandler((iq) => this._onRegisterIQ(iq), null, "iq", null, null);
|
||||
_converse.connection.send(iq);
|
||||
this.setFields(iq.tree());
|
||||
}
|
||||
|
@ -462,8 +410,8 @@ class RegisterPanel extends ElementView {
|
|||
}
|
||||
|
||||
_setFieldsFromXForm (xform) {
|
||||
this.title = xform.querySelector('title')?.textContent;
|
||||
this.instructions = xform.querySelector('instructions')?.textContent;
|
||||
this.title = xform.querySelector('title')?.textContent ?? '';
|
||||
this.instructions = xform.querySelector('instructions')?.textContent ?? '';
|
||||
xform.querySelectorAll('field').forEach(field => {
|
||||
const _var = field.getAttribute('var');
|
||||
if (_var) {
|
||||
|
|
61
src/plugins/register/styles/register.scss
Normal file
61
src/plugins/register/styles/register.scss
Normal file
|
@ -0,0 +1,61 @@
|
|||
@import "shared/styles/_mixins.scss";
|
||||
|
||||
converse-register-panel {
|
||||
.alert {
|
||||
margin: auto;
|
||||
max-width: 50vw;
|
||||
}
|
||||
}
|
||||
|
||||
#converse-register {
|
||||
@include fade-in;
|
||||
background-color: var(--controlbox-pane-background-color);
|
||||
|
||||
.title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
input {
|
||||
height: auto;
|
||||
}
|
||||
.input-group-text {
|
||||
color: var(--text-color);
|
||||
background-color: var(--controlbox-pane-background-color);
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
color: green;
|
||||
font-size: 90%;
|
||||
margin: 1.5em 0;
|
||||
}
|
||||
|
||||
.form-errors {
|
||||
color: var(--error-color);
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.provider-title {
|
||||
font-size: var(--font-size-huge);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.provider-score {
|
||||
width: 178px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.form-help .url {
|
||||
font-weight: bold;
|
||||
color: var(--link-color);
|
||||
}
|
||||
|
||||
.instructions {
|
||||
color: gray;
|
||||
font-size: 85%;
|
||||
&:hover {
|
||||
color: var(--controlbox-text-color);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,18 +4,19 @@ import { __ } from 'i18n';
|
|||
import { api } from '@converse/headless/core';
|
||||
import { html } from 'lit';
|
||||
|
||||
const tplFormRequest = () => {
|
||||
const tplFormRequest = (el) => {
|
||||
const default_domain = api.settings.get('registration_domain');
|
||||
const i18n_fetch_form = __("Hold tight, we're fetching the registration form…");
|
||||
const i18n_cancel = __('Cancel');
|
||||
return html`
|
||||
<form id="converse-register" class="converse-form no-scrolling">
|
||||
<form id="converse-register" class="converse-form no-scrolling" @submit=${ev => el.onFormSubmission(ev)}>
|
||||
${tplSpinner({ 'classes': 'hor_centered' })}
|
||||
<p class="info">${i18n_fetch_form}</p>
|
||||
${default_domain
|
||||
? ''
|
||||
: html`
|
||||
<button class="btn btn-secondary button-cancel hor_centered">${i18n_cancel}</button>
|
||||
<button class="btn btn-secondary button-cancel hor_centered"
|
||||
@click=${ev => el.renderProviderChoiceForm(ev)}>${i18n_cancel}</button>
|
||||
`}
|
||||
</form>
|
||||
`;
|
||||
|
@ -50,19 +51,21 @@ const tplFetchFormButtons = () => {
|
|||
`;
|
||||
};
|
||||
|
||||
const tplChooseProvider = () => {
|
||||
const tplChooseProvider = (el) => {
|
||||
const default_domain = api.settings.get('registration_domain');
|
||||
const i18n_create_account = __('Create your account');
|
||||
const i18n_choose_provider = __('Please enter the XMPP provider to register with:');
|
||||
const show_form_buttons = !default_domain && el.status === CHOOSE_PROVIDER;
|
||||
|
||||
return html`
|
||||
<form id="converse-register" class="converse-form">
|
||||
<form id="converse-register" class="converse-form" @submit=${ev => el.onFormSubmission(ev)}>
|
||||
<legend class="col-form-label">${i18n_create_account}</legend>
|
||||
<div class="form-group">
|
||||
<label>${i18n_choose_provider}</label>
|
||||
<div class="form-errors hidden"></div>
|
||||
|
||||
${default_domain ? default_domain : tplDomainInput()}
|
||||
</div>
|
||||
${default_domain ? '' : tplFetchFormButtons()}
|
||||
${show_form_buttons ? tplFetchFormButtons() : ''}
|
||||
</form>
|
||||
`;
|
||||
};
|
||||
|
@ -71,11 +74,12 @@ const CHOOSE_PROVIDER = 0;
|
|||
const FETCHING_FORM = 1;
|
||||
const REGISTRATION_FORM = 2;
|
||||
|
||||
export default o => {
|
||||
export default (el) => {
|
||||
return html`
|
||||
<converse-brand-logo></converse-brand-logo>
|
||||
${o.model.get('registration_status') === CHOOSE_PROVIDER ? tplChooseProvider() : ''}
|
||||
${o.model.get('registration_status') === FETCHING_FORM ? tplFormRequest() : ''}
|
||||
${o.model.get('registration_status') === REGISTRATION_FORM ? tplRegistrationForm(o) : ''}
|
||||
${ el.error_message ? html`<div class="alert alert-danger" role="alert">${el.error_message}</div>` : '' }
|
||||
${el.status === CHOOSE_PROVIDER ? tplChooseProvider(el) : ''}
|
||||
${el.status === FETCHING_FORM ? tplFormRequest(el) : ''}
|
||||
${el.status === REGISTRATION_FORM ? tplRegistrationForm(el) : ''}
|
||||
`;
|
||||
};
|
||||
|
|
|
@ -2,7 +2,7 @@ import { __ } from 'i18n';
|
|||
import { api } from '@converse/headless/core';
|
||||
import { html } from 'lit';
|
||||
|
||||
export default o => {
|
||||
export default (el) => {
|
||||
const i18n_choose_provider = __('Choose a different provider');
|
||||
const i18n_has_account = __('Already have a chat account?');
|
||||
const i18n_legend = __('Account Registration:');
|
||||
|
@ -11,15 +11,15 @@ export default o => {
|
|||
const registration_domain = api.settings.get('registration_domain');
|
||||
|
||||
return html`
|
||||
<form id="converse-register" class="converse-form">
|
||||
<legend class="col-form-label">${i18n_legend} ${o.domain}</legend>
|
||||
<p class="title">${o.title}</p>
|
||||
<p class="form-help instructions">${o.instructions}</p>
|
||||
<form id="converse-register" class="converse-form" @submit=${ev => el.onFormSubmission(ev)}>
|
||||
<legend class="col-form-label">${i18n_legend} ${el.domain}</legend>
|
||||
<p class="title">${el.title}</p>
|
||||
<p class="form-help instructions">${el.instructions}</p>
|
||||
<div class="form-errors hidden"></div>
|
||||
${o.form_fields}
|
||||
${el.form_fields}
|
||||
|
||||
<fieldset class="buttons form-group">
|
||||
${o.fields
|
||||
${el.fields
|
||||
? html`
|
||||
<input type="submit" class="btn btn-primary" value="${i18n_register}" />
|
||||
`
|
||||
|
@ -31,6 +31,7 @@ export default o => {
|
|||
type="button"
|
||||
class="btn btn-secondary button-cancel"
|
||||
value="${i18n_choose_provider}"
|
||||
@click=${ev => el.renderProviderChoiceForm(ev)}
|
||||
/>
|
||||
`}
|
||||
<div class="switch-form">
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
/*global mock, converse */
|
||||
|
||||
const Strophe = converse.env.Strophe;
|
||||
const $iq = converse.env.$iq;
|
||||
const { sizzle} = converse.env;
|
||||
const u = converse.env.utils;
|
||||
const { stx, Strophe, $iq, sizzle, u } = converse.env;
|
||||
|
||||
|
||||
describe("The Registration Panel", function () {
|
||||
|
||||
|
@ -88,6 +86,61 @@ describe("The Registration Panel", function () {
|
|||
delete _converse.connection;
|
||||
}));
|
||||
|
||||
it("allows the user to choose an XMPP provider's domain in fullscreen view mode",
|
||||
mock.initConverse(
|
||||
['chatBoxesInitialized'], {
|
||||
auto_login: false,
|
||||
view_mode: 'fullscreen',
|
||||
discover_connection_methods: false,
|
||||
allow_registration: true
|
||||
},
|
||||
async function (_converse) {
|
||||
|
||||
const cbview = _converse.api.controlbox.get();
|
||||
cbview.querySelector('.toggle-register-login').click();
|
||||
|
||||
const registerview = await u.waitUntil(() => cbview.querySelector('converse-register-panel'));
|
||||
spyOn(registerview, 'fetchRegistrationForm').and.callThrough();
|
||||
spyOn(registerview, 'onProviderChosen').and.callThrough();
|
||||
spyOn(registerview, 'getRegistrationFields').and.callThrough();
|
||||
spyOn(registerview, 'renderRegistrationForm').and.callThrough();
|
||||
|
||||
expect(registerview._registering).toBeFalsy();
|
||||
expect(_converse.api.connection.connected()).toBeFalsy();
|
||||
registerview.querySelector('input[name=domain]').value = 'conversejs.org';
|
||||
registerview.querySelector('input[type=submit]').click();
|
||||
expect(registerview.onProviderChosen).toHaveBeenCalled();
|
||||
expect(registerview._registering).toBeTruthy();
|
||||
|
||||
await u.waitUntil(() => registerview.fetchRegistrationForm.calls.count());
|
||||
|
||||
let stanza = new Strophe.Builder("stream:features", {
|
||||
'xmlns:stream': "http://etherx.jabber.org/streams",
|
||||
'xmlns': "jabber:client"
|
||||
})
|
||||
.c('register', {xmlns: "http://jabber.org/features/iq-register"}).up()
|
||||
.c('mechanisms', {xmlns: "urn:ietf:params:xml:ns:xmpp-sasl"});
|
||||
_converse.connection._connect_cb(mock.createRequest(stanza));
|
||||
|
||||
expect(registerview.getRegistrationFields).toHaveBeenCalled();
|
||||
|
||||
stanza = $iq({
|
||||
'type': 'result',
|
||||
'id': 'reg1'
|
||||
}).c('query', {'xmlns': 'jabber:iq:register'})
|
||||
.c('instructions')
|
||||
.t('Please choose a username, password and provide your email address').up()
|
||||
.c('username').up()
|
||||
.c('password').up()
|
||||
.c('email');
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
expect(registerview.renderRegistrationForm).toHaveBeenCalled();
|
||||
|
||||
await u.waitUntil(() => registerview.querySelectorAll('input').length === 5);
|
||||
expect(registerview.querySelectorAll('input[type=submit]').length).toBe(1);
|
||||
expect(registerview.querySelectorAll('input[type=button]').length).toBe(1);
|
||||
}));
|
||||
|
||||
it("will render a registration form as received from the XMPP provider",
|
||||
mock.initConverse(
|
||||
['chatBoxesInitialized'],
|
||||
|
@ -108,7 +161,6 @@ describe("The Registration Panel", function () {
|
|||
spyOn(registerview, 'getRegistrationFields').and.callThrough();
|
||||
spyOn(registerview, 'onRegistrationFields').and.callThrough();
|
||||
spyOn(registerview, 'renderRegistrationForm').and.callThrough();
|
||||
registerview.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
|
||||
|
||||
expect(registerview._registering).toBeFalsy();
|
||||
expect(_converse.api.connection.connected()).toBeFalsy();
|
||||
|
@ -140,7 +192,8 @@ describe("The Registration Panel", function () {
|
|||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
expect(registerview.onRegistrationFields).toHaveBeenCalled();
|
||||
expect(registerview.renderRegistrationForm).toHaveBeenCalled();
|
||||
expect(registerview.querySelectorAll('input').length).toBe(5);
|
||||
|
||||
await u.waitUntil(() => registerview.querySelectorAll('input').length === 5);
|
||||
expect(registerview.querySelectorAll('input[type=submit]').length).toBe(1);
|
||||
expect(registerview.querySelectorAll('input[type=button]').length).toBe(1);
|
||||
}));
|
||||
|
@ -168,7 +221,6 @@ describe("The Registration Panel", function () {
|
|||
spyOn(registerview, 'getRegistrationFields').and.callThrough();
|
||||
spyOn(registerview, 'onRegistrationFields').and.callThrough();
|
||||
spyOn(registerview, 'renderRegistrationForm').and.callThrough();
|
||||
registerview.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
|
||||
|
||||
registerview.querySelector('input[name=domain]').value = 'conversejs.org';
|
||||
registerview.querySelector('input[type=submit]').click();
|
||||
|
@ -192,7 +244,9 @@ describe("The Registration Panel", function () {
|
|||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
expect(registerview.form_type).toBe('legacy');
|
||||
|
||||
registerview.querySelector('input[name=username]').value = 'testusername';
|
||||
const username_input = await u.waitUntil(() => registerview.querySelector('input[name=username]'));
|
||||
|
||||
username_input.value = 'testusername';
|
||||
registerview.querySelector('input[name=password]').value = 'testpassword';
|
||||
registerview.querySelector('input[name=email]').value = 'test@email.local';
|
||||
|
||||
|
@ -229,7 +283,6 @@ describe("The Registration Panel", function () {
|
|||
spyOn(registerview, 'getRegistrationFields').and.callThrough();
|
||||
spyOn(registerview, 'onRegistrationFields').and.callThrough();
|
||||
spyOn(registerview, 'renderRegistrationForm').and.callThrough();
|
||||
registerview.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
|
||||
|
||||
registerview.querySelector('input[name=domain]').value = 'conversejs.org';
|
||||
registerview.querySelector('input[type=submit]').click();
|
||||
|
@ -255,7 +308,9 @@ describe("The Registration Panel", function () {
|
|||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
expect(registerview.form_type).toBe('xform');
|
||||
|
||||
registerview.querySelector('input[name=username]').value = 'testusername';
|
||||
const username_input = await u.waitUntil(() => registerview.querySelector('input[name=username]'));
|
||||
|
||||
username_input.value = 'testusername';
|
||||
registerview.querySelector('input[name=password]').value = 'testpassword';
|
||||
registerview.querySelector('input[name=email]').value = 'test@email.local';
|
||||
|
||||
|
@ -304,12 +359,6 @@ describe("The Registration Panel", function () {
|
|||
const cbview = _converse.chatboxviews.get('controlbox');
|
||||
cbview.querySelector('.toggle-register-login').click();
|
||||
const registerview = await u.waitUntil(() => cbview.querySelector('converse-register-panel'));
|
||||
spyOn(registerview, 'onProviderChosen').and.callThrough();
|
||||
spyOn(registerview, 'getRegistrationFields').and.callThrough();
|
||||
spyOn(registerview, 'onRegistrationFields').and.callThrough();
|
||||
spyOn(registerview, 'renderRegistrationForm').and.callThrough();
|
||||
registerview.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
|
||||
|
||||
registerview.querySelector('input[name=domain]').value = 'conversejs.org';
|
||||
registerview.querySelector('input[type=submit]').click();
|
||||
|
||||
|
@ -321,7 +370,7 @@ describe("The Registration Panel", function () {
|
|||
.c('mechanisms', {xmlns: "urn:ietf:params:xml:ns:xmpp-sasl"});
|
||||
_converse.connection._connect_cb(mock.createRequest(stanza));
|
||||
|
||||
stanza = u.toStanza(`
|
||||
stanza = stx`
|
||||
<iq xmlns="jabber:client" type="result" from="conversations.im" id="ad1e0d50-5adb-4397-a997-5feab56fe418:sendIQ" xml:lang="en">
|
||||
<query xmlns="jabber:iq:register">
|
||||
<x xmlns="jabber:x:data" type="form">
|
||||
|
@ -344,13 +393,76 @@ describe("The Registration Panel", function () {
|
|||
max-age="0">iVBORw0KGgoAAAANSUhEUgAAALQAAAA8BAMAAAA9AI20AAAAMFBMVEX///8AAADf39+fn59fX19/f3+/v78fHx8/Pz9PT08bGxsvLy9jY2NTU1MXFxcnJyc84bkWAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAERUlEQVRYhe1WTXMaRxDdDxY4JWpYvDinpVyxdATLin0MiRLlCHEi+7hYUcVHTSI7urhK6yr5//gn5N/4Z7inX89+CQkTcFUO6gOwS8/r7tdvesbzvoT5ROR5JJ9bB97xAK22XWAY1WznlnUr7QaAzSOsWufXQ6wH/FmO60b4D936LJr8TWRwW4SNgOsodZr8m4vZUoRt2xZ3xHXgna1FCE5+f5aWwPU//bXgg8eHjyqPp4aXJeOlwLUIt0O39zOvPWW3WfHmCCkli816FxlK0rnFGKZ484dN+eIXsw1R+G+JfjwgOpMnm+r5SxA63gS2Q8MchO1RLN8jSn4W4F5OPed2evhTthKLG3bsfjLL874XGBpWHLrU0953i/ev7JsfViHbhsWSQTunJDOppeAe0hVGokJUHBOphmjrbBlgabviJKXbIP0B//gKSBHZh2rvJnQp3wsapMFz+VsTPNhPr0Hn9N57YOjywaxFSU6S79fUF39KBDgnt6yjZOeSffk+4IXDZovbQl9E96m34EzQKMepQcbzijAGiBmDsO+LaqzqG3m3kEf+DQ2mY+vdk5c2n2Iaj5QGi6n59FHDmcuP4t8MGlRaF39P6ENyIaB2EXdpjLnQq9IgdVxfax3ilBc10u4gowX9K6BaKiZNmCC7CF/WpkJvWxN00OjuoqGYLqAnpILLE68Ymrt9M0S9hcznUJ8RykdlLalUfFaDjvA8pT2kxmsl5fuMaM6mSWUpUhDoudSucdhiZFDwphEHwsMwhEpH0jsm+/UBK2wCzFIiitalN7YjWkyIBgTNPgpDXX4rjk4UH+yPPgfK4HNZQCP/KZ0fGnrnKl8+pXl3X7FwZuwNUdwDGO+BjPUn6XaKtbkm+MJ6vtaXSnIz6wBT/m+VvZNIhz7ayabQLSeRQDmYkjt0KlmHDa555v9DzFxx+CCvCG4K3dbx6mTYtfPs1Dgdh0i3W+cl4lnnhblMKKBBA23X1Ezc3E5ZoPS5KHjPiU1rKTviYe1fTsa6e3UwXGWI4ykB8uiGqkmA6Cbf3K4JTH3LOBlbX+yPWll57LKVeH8CTEvyVPV2TXL8kPnPqtA51CaFYxOH2rJoZunSnvsSj48WiaDccl6KEgiMSarITsa+rWWBnqFloYlT1qWW2GKw9nPSbEvoVHFst967XgNQjxdA66Q6VFEUh488xfaSo7cHB52XYzA4eRlVteeT8ostWfuPea0oF6MwzlwgZE9gQI+uUV0gzK+WlpUrNI8juhhX/OyNwZnRrsDfxOqS1aDR+gC6NUPvJpvQeVZ9eiNr9aDUuddY3bLnA4tH4r/49UboznH1ia8PV/uP3WUB3dxtzj1uxfDZgbEbZx17Itwrf0Jyc8N4en+5dhivtKeYjGJ8yXgUzKvSU/uWJZmsuAYtseDku+K3zMHi4lC1h0suPmtZaEp2tm3hEV2lXwb6zu7szv6f9glF5rPGT5xR7AAAAABJRU5ErkJggg==</data>
|
||||
<instructions>You need a client that supports x:data and CAPTCHA to register</instructions>
|
||||
</query>
|
||||
</iq>`);
|
||||
</iq>`;
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
|
||||
await u.waitUntil(() => registerview.querySelectorAll('#converse-register input[required]').length === 3);
|
||||
expect(registerview.form_type).toBe('xform');
|
||||
expect(registerview.querySelectorAll('#converse-register input[required]').length).toBe(3);
|
||||
// Hide the controlbox so that we can see whether the test
|
||||
// passed or failed
|
||||
u.addClass('hidden', _converse.chatboxviews.get('controlbox').el);
|
||||
|
||||
// Hide the controlbox so that we can see whether the test passed or failed
|
||||
u.addClass('hidden', _converse.chatboxviews.get('controlbox'));
|
||||
delete _converse.connection;
|
||||
}));
|
||||
|
||||
it("lets you choose a different provider",
|
||||
mock.initConverse(
|
||||
['chatBoxesInitialized'],
|
||||
{ auto_login: false,
|
||||
view_mode: 'fullscreen',
|
||||
discover_connection_methods: false,
|
||||
allow_registration: true },
|
||||
async function (_converse) {
|
||||
|
||||
const toggle = document.querySelector(".toggle-controlbox");
|
||||
if (!u.isVisible(document.querySelector("#controlbox"))) {
|
||||
if (!u.isVisible(toggle)) {
|
||||
u.removeClass('hidden', toggle);
|
||||
}
|
||||
toggle.click();
|
||||
}
|
||||
const cbview = _converse.chatboxviews.get('controlbox');
|
||||
cbview.querySelector('.toggle-register-login').click();
|
||||
const registerview = await u.waitUntil(() => cbview.querySelector('converse-register-panel'));
|
||||
|
||||
registerview.querySelector('input[name=domain]').value = 'conversejs.org';
|
||||
registerview.querySelector('input[type=submit]').click();
|
||||
|
||||
let stanza = new Strophe.Builder("stream:features", {
|
||||
'xmlns:stream': "http://etherx.jabber.org/streams",
|
||||
'xmlns': "jabber:client"
|
||||
})
|
||||
.c('register', {xmlns: "http://jabber.org/features/iq-register"}).up()
|
||||
.c('mechanisms', {xmlns: "urn:ietf:params:xml:ns:xmpp-sasl"});
|
||||
_converse.connection._connect_cb(mock.createRequest(stanza));
|
||||
|
||||
stanza = stx`
|
||||
<iq xmlns="jabber:client" type="result" from="conversations.im" id="ad1e0d50-5adb-4397-a997-5feab56fe418:sendIQ" xml:lang="en">
|
||||
<query xmlns="jabber:iq:register">
|
||||
<x xmlns="jabber:x:data" type="form">
|
||||
<instructions>Choose a username and password to register with this server</instructions>
|
||||
<field var="FORM_TYPE" type="hidden"><value>urn:xmpp:captcha</value></field>
|
||||
<field var="username" type="text-single" label="User"><required/></field>
|
||||
<field var="password" type="text-private" label="Password"><required/></field>
|
||||
<field var="from" type="hidden"><value>conversations.im</value></field>
|
||||
<field var="challenge" type="hidden"><value>15376320046808160053</value></field>
|
||||
<field var="sid" type="hidden"><value>ad1e0d50-5adb-4397-a997-5feab56fe418:sendIQ</value></field>
|
||||
</x>
|
||||
</query>
|
||||
</iq>`;
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
|
||||
await u.waitUntil(() => registerview.querySelectorAll('#converse-register input[required]').length === 2);
|
||||
expect(registerview.form_type).toBe('xform');
|
||||
|
||||
const button = await u.waitUntil(() => registerview.querySelector('.btn-secondary'));
|
||||
expect(button.value).toBe("Choose a different provider");
|
||||
button.click();
|
||||
|
||||
await u.waitUntil(() => registerview.querySelector('input[name="domain"]'));
|
||||
expect(registerview.querySelectorAll('input[required]').length).toBe(1);
|
||||
|
||||
// Hide the controlbox so that we can see whether the test passed or failed
|
||||
u.addClass('hidden', _converse.chatboxviews.get('controlbox'));
|
||||
delete _converse.connection;
|
||||
}));
|
||||
|
||||
|
@ -385,7 +497,7 @@ describe("The Registration Panel", function () {
|
|||
.c('mechanisms', {xmlns: "urn:ietf:params:xml:ns:xmpp-sasl"});
|
||||
_converse.connection._connect_cb(mock.createRequest(stanza));
|
||||
|
||||
stanza = u.toStanza(`
|
||||
stanza = stx`
|
||||
<iq xmlns="jabber:client" type="result" from="conversejs.org" id="ad1e0d50-5adb-4397-a997-5feab56fe418:sendIQ" xml:lang="en">
|
||||
<query xmlns="jabber:iq:register">
|
||||
<x xmlns="jabber:x:data" type="form">
|
||||
|
@ -408,11 +520,12 @@ describe("The Registration Panel", function () {
|
|||
max-age="0">iVBORw0KGgoAAAANSUhEUgAAALQAAAA8BAMAAAA9AI20AAAAMFBMVEX///8AAADf39+fn59fX19/f3+/v78fHx8/Pz9PT08bGxsvLy9jY2NTU1MXFxcnJyc84bkWAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAERUlEQVRYhe1WTXMaRxDdDxY4JWpYvDinpVyxdATLin0MiRLlCHEi+7hYUcVHTSI7urhK6yr5//gn5N/4Z7inX89+CQkTcFUO6gOwS8/r7tdvesbzvoT5ROR5JJ9bB97xAK22XWAY1WznlnUr7QaAzSOsWufXQ6wH/FmO60b4D936LJr8TWRwW4SNgOsodZr8m4vZUoRt2xZ3xHXgna1FCE5+f5aWwPU//bXgg8eHjyqPp4aXJeOlwLUIt0O39zOvPWW3WfHmCCkli816FxlK0rnFGKZ484dN+eIXsw1R+G+JfjwgOpMnm+r5SxA63gS2Q8MchO1RLN8jSn4W4F5OPed2evhTthKLG3bsfjLL874XGBpWHLrU0953i/ev7JsfViHbhsWSQTunJDOppeAe0hVGokJUHBOphmjrbBlgabviJKXbIP0B//gKSBHZh2rvJnQp3wsapMFz+VsTPNhPr0Hn9N57YOjywaxFSU6S79fUF39KBDgnt6yjZOeSffk+4IXDZovbQl9E96m34EzQKMepQcbzijAGiBmDsO+LaqzqG3m3kEf+DQ2mY+vdk5c2n2Iaj5QGi6n59FHDmcuP4t8MGlRaF39P6ENyIaB2EXdpjLnQq9IgdVxfax3ilBc10u4gowX9K6BaKiZNmCC7CF/WpkJvWxN00OjuoqGYLqAnpILLE68Ymrt9M0S9hcznUJ8RykdlLalUfFaDjvA8pT2kxmsl5fuMaM6mSWUpUhDoudSucdhiZFDwphEHwsMwhEpH0jsm+/UBK2wCzFIiitalN7YjWkyIBgTNPgpDXX4rjk4UH+yPPgfK4HNZQCP/KZ0fGnrnKl8+pXl3X7FwZuwNUdwDGO+BjPUn6XaKtbkm+MJ6vtaXSnIz6wBT/m+VvZNIhz7ayabQLSeRQDmYkjt0KlmHDa555v9DzFxx+CCvCG4K3dbx6mTYtfPs1Dgdh0i3W+cl4lnnhblMKKBBA23X1Ezc3E5ZoPS5KHjPiU1rKTviYe1fTsa6e3UwXGWI4ykB8uiGqkmA6Cbf3K4JTH3LOBlbX+yPWll57LKVeH8CTEvyVPV2TXL8kPnPqtA51CaFYxOH2rJoZunSnvsSj48WiaDccl6KEgiMSarITsa+rWWBnqFloYlT1qWW2GKw9nPSbEvoVHFst967XgNQjxdA66Q6VFEUh488xfaSo7cHB52XYzA4eRlVteeT8ostWfuPea0oF6MwzlwgZE9gQI+uUV0gzK+WlpUrNI8juhhX/OyNwZnRrsDfxOqS1aDR+gC6NUPvJpvQeVZ9eiNr9aDUuddY3bLnA4tH4r/49UboznH1ia8PV/uP3WUB3dxtzj1uxfDZgbEbZx17Itwrf0Jyc8N4en+5dhivtKeYjGJ8yXgUzKvSU/uWJZmsuAYtseDku+K3zMHi4lC1h0suPmtZaEp2tm3hEV2lXwb6zu7szv6f9glF5rPGT5xR7AAAAABJRU5ErkJggg==</data>
|
||||
<instructions>You need a client that supports x:data and CAPTCHA to register</instructions>
|
||||
</query>
|
||||
</iq>`);
|
||||
</iq>`;
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
|
||||
spyOn(view, 'submitRegistrationForm').and.callThrough();
|
||||
const username_input = view.querySelector('[name="username"]');
|
||||
|
||||
const username_input = await u.waitUntil(() => view.querySelector('[name="username"]'));
|
||||
username_input.value = 'romeo';
|
||||
const password_input = view.querySelector('[name="password"]');
|
||||
password_input.value = 'secret';
|
||||
|
@ -421,16 +534,20 @@ describe("The Registration Panel", function () {
|
|||
view.querySelector('[type="submit"]').click();
|
||||
expect(view.submitRegistrationForm).toHaveBeenCalled();
|
||||
|
||||
const response_IQ = u.toStanza(`
|
||||
<iq xml:lang='en' from='conversejs.org' type='error' id='d9917b7a-588f-4ef6-8a56-0d6d3ad538ae:sendIQ'>
|
||||
const response_IQ = stx`
|
||||
<iq xml:lang='en' from='conversejs.org' type='error' id='d9917b7a-588f-4ef6-8a56-0d6d3ad538ae:sendIQ' xmlns="jabber:client">
|
||||
<query xmlns='jabber:iq:register'/>
|
||||
<error code='500' type='wait'>
|
||||
<resource-constraint xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
|
||||
<text xml:lang='en' xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'>Too many CAPTCHA requests</text>
|
||||
</error>
|
||||
</iq>`);
|
||||
</iq>`;
|
||||
_converse.connection._dataRecv(mock.createRequest(response_IQ));
|
||||
expect(view.querySelector('.error')?.textContent.trim()).toBe('Too many CAPTCHA requests');
|
||||
|
||||
const alert = await u.waitUntil(() => view.querySelector('.alert'));
|
||||
expect(alert.textContent.trim()).toBe('Too many CAPTCHA requests');
|
||||
// Hide the controlbox so that we can see whether the test passed or failed
|
||||
u.addClass('hidden', _converse.chatboxviews.get('controlbox'));
|
||||
delete _converse.connection;
|
||||
}));
|
||||
});
|
||||
|
|
7
src/plugins/register/utils.js
Normal file
7
src/plugins/register/utils.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
import { _converse, api } from '@converse/headless/core';
|
||||
|
||||
export async function setActiveForm (value) {
|
||||
await api.waitUntil('controlBoxInitialized');
|
||||
const controlbox = _converse.chatboxes.get('controlbox');
|
||||
controlbox.set({ 'active-form': value });
|
||||
}
|
|
@ -4,12 +4,13 @@ export default (o) => html`
|
|||
<div class="form-group">
|
||||
${ o.label ? html`<label>${o.label}</label>` : '' }
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<input name="${o.name}"
|
||||
class="form-control"
|
||||
type="${o.type}"
|
||||
value="${o.value || ''}"
|
||||
?required="${o.required}" />
|
||||
<div class="input-group-text col" title="${o.domain}">${o.domain}</div>
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-text" title="${o.domain}">${o.domain}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
|
Loading…
Reference in New Issue
Block a user