summaryrefslogtreecommitdiff
path: root/addons/snailmail/static/src/components/message
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/snailmail/static/src/components/message
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/snailmail/static/src/components/message')
-rw-r--r--addons/snailmail/static/src/components/message/message.js87
-rw-r--r--addons/snailmail/static/src/components/message/message.xml37
-rw-r--r--addons/snailmail/static/src/components/message/message_tests.js680
3 files changed, 804 insertions, 0 deletions
diff --git a/addons/snailmail/static/src/components/message/message.js b/addons/snailmail/static/src/components/message/message.js
new file mode 100644
index 00000000..1033c901
--- /dev/null
+++ b/addons/snailmail/static/src/components/message/message.js
@@ -0,0 +1,87 @@
+odoo.define('snailmail/static/src/components/message/message.js', function (require) {
+'use strict';
+
+const components = {
+ Message: require('mail/static/src/components/message/message.js'),
+ SnailmailErrorDialog: require('snailmail/static/src/components/snailmail_error_dialog/snailmail_error_dialog.js'),
+ SnailmailNotificationPopover: require('snailmail/static/src/components/snailmail_notification_popover/snailmail_notification_popover.js'),
+};
+
+const { patch } = require('web.utils');
+
+const { useState } = owl;
+
+Object.assign(components.Message.components, {
+ SnailmailErrorDialog: components.SnailmailErrorDialog,
+ SnailmailNotificationPopover: components.SnailmailNotificationPopover,
+});
+
+patch(components.Message, 'snailmail/static/src/components/message/message.js', {
+ /**
+ * @override
+ */
+ _constructor() {
+ this._super(...arguments);
+ this.snailmailState = useState({
+ // Determine if the error dialog is displayed.
+ hasDialog: false,
+ });
+ },
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * @override
+ */
+ _onClickFailure() {
+ if (this.message.message_type === 'snailmail') {
+ /**
+ * Messages from snailmail are considered to have at most one
+ * notification. The failure type of the whole message is considered
+ * to be the same as the one from that first notification, and the
+ * click action will depend on it.
+ */
+ switch (this.message.notifications[0].failure_type) {
+ case 'sn_credit':
+ // URL only used in this component, not received at init
+ this.env.messaging.fetchSnailmailCreditsUrl();
+ this.snailmailState.hasDialog = true;
+ break;
+ case 'sn_error':
+ this.snailmailState.hasDialog = true;
+ break;
+ case 'sn_fields':
+ this.message.openMissingFieldsLetterAction();
+ break;
+ case 'sn_format':
+ this.message.openFormatLetterAction();
+ break;
+ case 'sn_price':
+ this.snailmailState.hasDialog = true;
+ break;
+ case 'sn_trial':
+ // URL only used in this component, not received at init
+ this.env.messaging.fetchSnailmailCreditsUrlTrial();
+ this.snailmailState.hasDialog = true;
+ break;
+ }
+ } else {
+ this._super(...arguments);
+ }
+ },
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * @private
+ */
+ _onDialogClosedSnailmailError() {
+ this.snailmailState.hasDialog = false;
+ },
+});
+
+});
diff --git a/addons/snailmail/static/src/components/message/message.xml b/addons/snailmail/static/src/components/message/message.xml
new file mode 100644
index 00000000..d563083f
--- /dev/null
+++ b/addons/snailmail/static/src/components/message/message.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<templates xml:space="preserve">
+
+ <t t-inherit="mail.Message" t-inherit-mode="extension">
+ <xpath expr="//*[@name='failureIcon']" position="replace">
+ <t t-if="message.message_type === 'snailmail'">
+ <i class="o_Message_notificationIcon fa fa-paper-plane"/>
+ </t>
+ <t t-else="">$0</t>
+ </xpath>
+
+ <xpath expr="//*[@name='notificationIcon']" position="replace">
+ <t t-if="message.message_type === 'snailmail'">
+ <i class="o_Message_notificationIcon fa fa-paper-plane"/>
+ </t>
+ <t t-else="">$0</t>
+ </xpath>
+
+ <xpath expr="//*[@name='rootCondition']" position="inside">
+ <t t-if="snailmailState.hasDialog">
+ <SnailmailErrorDialog messageLocalId="message.localId" t-on-dialog-closed="_onDialogClosedSnailmailError"/>
+ </t>
+ </xpath>
+
+ <!--
+ It was decided that the information displayed for snailmail messages
+ has to be different than for standard messages, see task-1907998.
+ -->
+ <xpath expr="//NotificationPopover" position="replace">
+ <t t-if="message.message_type === 'snailmail'">
+ <SnailmailNotificationPopover messageLocalId="message.localId"/>
+ </t>
+ <t t-else="">$0</t>
+ </xpath>
+ </t>
+
+</templates>
diff --git a/addons/snailmail/static/src/components/message/message_tests.js b/addons/snailmail/static/src/components/message/message_tests.js
new file mode 100644
index 00000000..67044509
--- /dev/null
+++ b/addons/snailmail/static/src/components/message/message_tests.js
@@ -0,0 +1,680 @@
+odoo.define('snailmail/static/src/components/message/message_tests.js', function (require) {
+'use strict';
+
+const components = {
+ Message: require('mail/static/src/components/message/message.js'),
+};
+const {
+ afterEach,
+ afterNextRender,
+ beforeEach,
+ createRootComponent,
+ start,
+} = require('mail/static/src/utils/test_utils.js');
+
+const Bus = require('web.Bus');
+
+QUnit.module('snailmail', {}, function () {
+QUnit.module('components', {}, function () {
+QUnit.module('message', {}, function () {
+QUnit.module('message_tests.js', {
+ beforeEach() {
+ beforeEach(this);
+
+ this.createMessageComponent = async (message, otherProps) => {
+ const props = Object.assign({ messageLocalId: message.localId }, otherProps);
+ await createRootComponent(this, components.Message, {
+ props,
+ target: this.widget.el,
+ });
+ };
+
+ this.start = async params => {
+ const { env, widget } = await start(Object.assign({}, params, {
+ data: this.data,
+ }));
+ this.env = env;
+ this.widget = widget;
+ };
+ },
+ afterEach() {
+ afterEach(this);
+ },
+});
+
+QUnit.test('Sent', async function (assert) {
+ assert.expect(8);
+
+ await this.start();
+ const threadViewer = this.env.models['mail.thread_viewer'].create({
+ hasThreadView: true,
+ thread: [['create', {
+ id: 11,
+ model: 'mail.channel',
+ }]],
+ });
+ const message = this.env.models['mail.message'].create({
+ id: 10,
+ message_type: 'snailmail',
+ notifications: [['insert', {
+ id: 11,
+ notification_status: 'sent',
+ notification_type: 'snail',
+ }]],
+ originThread: [['link', threadViewer.thread]],
+ });
+ await this.createMessageComponent(message, {
+ threadViewLocalId: threadViewer.threadView.localId
+ });
+
+ assert.containsOnce(
+ document.body,
+ '.o_Message',
+ "should display a message component"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_Message_notificationIconClickable',
+ "should display the notification icon container"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_Message_notificationIcon',
+ "should display the notification icon"
+ );
+ assert.hasClass(
+ document.querySelector('.o_Message_notificationIcon'),
+ 'fa-paper-plane',
+ "icon should represent snailmail"
+ );
+
+ await afterNextRender(() => {
+ document.querySelector('.o_Message_notificationIconClickable').click();
+ });
+ assert.containsOnce(
+ document.body,
+ '.o_SnailmailNotificationPopover',
+ "notification popover should be open"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_SnailmailNotificationPopover_icon',
+ "popover should have one icon"
+ );
+ assert.hasClass(
+ document.querySelector('.o_SnailmailNotificationPopover_icon'),
+ 'fa-check',
+ "popover should have the sent icon"
+ );
+ assert.strictEqual(
+ document.querySelector('.o_SnailmailNotificationPopover').textContent.trim(),
+ "Sent",
+ "popover should have the sent text"
+ );
+});
+
+QUnit.test('Canceled', async function (assert) {
+ assert.expect(8);
+
+ await this.start();
+ const threadViewer = this.env.models['mail.thread_viewer'].create({
+ hasThreadView: true,
+ thread: [['create', {
+ id: 11,
+ model: 'mail.channel',
+ }]],
+ });
+ const message = this.env.models['mail.message'].create({
+ id: 10,
+ message_type: 'snailmail',
+ notifications: [['insert', {
+ id: 11,
+ notification_status: 'canceled',
+ notification_type: 'snail',
+ }]],
+ originThread: [['link', threadViewer.thread]],
+ });
+ await this.createMessageComponent(message, {
+ threadViewLocalId: threadViewer.threadView.localId
+ });
+
+ assert.containsOnce(
+ document.body,
+ '.o_Message',
+ "should display a message component"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_Message_notificationIconClickable',
+ "should display the notification icon container"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_Message_notificationIcon',
+ "should display the notification icon"
+ );
+ assert.hasClass(
+ document.querySelector('.o_Message_notificationIcon'),
+ 'fa-paper-plane',
+ "icon should represent snailmail"
+ );
+
+ await afterNextRender(() => {
+ document.querySelector('.o_Message_notificationIconClickable').click();
+ });
+ assert.containsOnce(
+ document.body,
+ '.o_SnailmailNotificationPopover',
+ "notification popover should be open"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_SnailmailNotificationPopover_icon',
+ "popover should have one icon"
+ );
+ assert.hasClass(
+ document.querySelector('.o_SnailmailNotificationPopover_icon'),
+ 'fa-trash-o',
+ "popover should have the canceled icon"
+ );
+ assert.strictEqual(
+ document.querySelector('.o_SnailmailNotificationPopover').textContent.trim(),
+ "Canceled",
+ "popover should have the canceled text"
+ );
+});
+
+QUnit.test('Pending', async function (assert) {
+ assert.expect(8);
+
+ await this.start();
+ const threadViewer = this.env.models['mail.thread_viewer'].create({
+ hasThreadView: true,
+ thread: [['create', {
+ id: 11,
+ model: 'mail.channel',
+ }]],
+ });
+ const message = this.env.models['mail.message'].create({
+ id: 10,
+ message_type: 'snailmail',
+ notifications: [['insert', {
+ id: 11,
+ notification_status: 'ready',
+ notification_type: 'snail',
+ }]],
+ originThread: [['link', threadViewer.thread]],
+ });
+ await this.createMessageComponent(message, {
+ threadViewLocalId: threadViewer.threadView.localId
+ });
+
+ assert.containsOnce(
+ document.body,
+ '.o_Message',
+ "should display a message component"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_Message_notificationIconClickable',
+ "should display the notification icon container"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_Message_notificationIcon',
+ "should display the notification icon"
+ );
+ assert.hasClass(
+ document.querySelector('.o_Message_notificationIcon'),
+ 'fa-paper-plane',
+ "icon should represent snailmail"
+ );
+
+ await afterNextRender(() => {
+ document.querySelector('.o_Message_notificationIconClickable').click();
+ });
+ assert.containsOnce(
+ document.body,
+ '.o_SnailmailNotificationPopover',
+ "notification popover should be open"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_SnailmailNotificationPopover_icon',
+ "popover should have one icon"
+ );
+ assert.hasClass(
+ document.querySelector('.o_SnailmailNotificationPopover_icon'),
+ 'fa-clock-o',
+ "popover should have the pending icon"
+ );
+ assert.strictEqual(
+ document.querySelector('.o_SnailmailNotificationPopover').textContent.trim(),
+ "Awaiting Dispatch",
+ "popover should have the pending text"
+ );
+});
+
+QUnit.test('No Price Available', async function (assert) {
+ assert.expect(10);
+
+ await this.start({
+ async mockRPC(route, args) {
+ if (args.method === 'cancel_letter' && args.model === 'mail.message' && args.args[0][0] === 10) {
+ assert.step(args.method);
+ }
+ return this._super(...arguments);
+ },
+ });
+ const threadViewer = this.env.models['mail.thread_viewer'].create({
+ hasThreadView: true,
+ thread: [['create', {
+ id: 11,
+ model: 'mail.channel',
+ }]],
+ });
+ const message = this.env.models['mail.message'].create({
+ id: 10,
+ message_type: 'snailmail',
+ notifications: [['insert', {
+ failure_type: 'sn_price',
+ id: 11,
+ notification_status: 'exception',
+ notification_type: 'snail',
+ }]],
+ originThread: [['link', threadViewer.thread]],
+ });
+ await this.createMessageComponent(message, {
+ threadViewLocalId: threadViewer.threadView.localId
+ });
+
+ assert.containsOnce(
+ document.body,
+ '.o_Message',
+ "should display a message component"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_Message_notificationIconClickable',
+ "should display the notification icon container"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_Message_notificationIcon',
+ "should display the notification icon"
+ );
+ assert.hasClass(
+ document.querySelector('.o_Message_notificationIcon'),
+ 'fa-paper-plane',
+ "icon should represent snailmail"
+ );
+
+ await afterNextRender(() => {
+ document.querySelector('.o_Message_notificationIconClickable').click();
+ });
+ assert.containsOnce(
+ document.body,
+ '.o_SnailmailErrorDialog',
+ "error dialog should be open"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_SnailmailErrorDialog_contentPrice',
+ "error dialog should have the 'no price' content"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_SnailmailErrorDialog_cancelLetterButton',
+ "dialog should have a 'Cancel letter' button"
+ );
+
+ await afterNextRender(() => {
+ document.querySelector('.o_SnailmailErrorDialog_cancelLetterButton').click();
+ });
+ assert.containsNone(
+ document.body,
+ '.o_SnailmailErrorDialog',
+ "dialog should be closed after click on 'Cancel letter'"
+ );
+ assert.verifySteps(
+ ['cancel_letter'],
+ "should have made a RPC call to 'cancel_letter'"
+ );
+});
+
+QUnit.test('Credit Error', async function (assert) {
+ assert.expect(11);
+
+ await this.start({
+ async mockRPC(route, args) {
+ if (args.method === 'send_letter' && args.model === 'mail.message' && args.args[0][0] === 10) {
+ assert.step(args.method);
+ }
+ return this._super(...arguments);
+ },
+ });
+ const threadViewer = this.env.models['mail.thread_viewer'].create({
+ hasThreadView: true,
+ thread: [['create', {
+ id: 11,
+ model: 'mail.channel',
+ }]],
+ });
+ const message = this.env.models['mail.message'].create({
+ id: 10,
+ message_type: 'snailmail',
+ notifications: [['insert', {
+ failure_type: 'sn_credit',
+ id: 11,
+ notification_status: 'exception',
+ notification_type: 'snail',
+ }]],
+ originThread: [['link', threadViewer.thread]],
+ });
+ await this.createMessageComponent(message, {
+ threadViewLocalId: threadViewer.threadView.localId
+ });
+
+ assert.containsOnce(
+ document.body,
+ '.o_Message',
+ "should display a message component"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_Message_notificationIconClickable',
+ "should display the notification icon container"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_Message_notificationIcon',
+ "should display the notification icon"
+ );
+ assert.hasClass(
+ document.querySelector('.o_Message_notificationIcon'),
+ 'fa-paper-plane',
+ "icon should represent snailmail"
+ );
+
+ await afterNextRender(() => {
+ document.querySelector('.o_Message_notificationIconClickable').click();
+ });
+ assert.containsOnce(
+ document.body,
+ '.o_SnailmailErrorDialog',
+ "error dialog should be open"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_SnailmailErrorDialog_contentCredit',
+ "error dialog should have the 'credit' content"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_SnailmailErrorDialog_resendLetterButton',
+ "dialog should have a 'Re-send letter' button"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_SnailmailErrorDialog_cancelLetterButton',
+ "dialog should have a 'Cancel letter' button"
+ );
+
+ await afterNextRender(() => {
+ document.querySelector('.o_SnailmailErrorDialog_resendLetterButton').click();
+ });
+ assert.containsNone(
+ document.body,
+ '.o_SnailmailErrorDialog',
+ "dialog should be closed after click on 'Re-send letter'"
+ );
+ assert.verifySteps(
+ ['send_letter'],
+ "should have made a RPC call to 'send_letter'"
+ );
+});
+
+QUnit.test('Trial Error', async function (assert) {
+ assert.expect(11);
+
+ await this.start({
+ async mockRPC(route, args) {
+ if (args.method === 'send_letter' && args.model === 'mail.message' && args.args[0][0] === 10) {
+ assert.step(args.method);
+ }
+ return this._super(...arguments);
+ },
+ });
+ const threadViewer = this.env.models['mail.thread_viewer'].create({
+ hasThreadView: true,
+ thread: [['create', {
+ id: 11,
+ model: 'mail.channel',
+ }]],
+ });
+ const message = this.env.models['mail.message'].create({
+ id: 10,
+ message_type: 'snailmail',
+ notifications: [['insert', {
+ failure_type: 'sn_trial',
+ id: 11,
+ notification_status: 'exception',
+ notification_type: 'snail',
+ }]],
+ originThread: [['link', threadViewer.thread]],
+ });
+ await this.createMessageComponent(message, {
+ threadViewLocalId: threadViewer.threadView.localId
+ });
+
+ assert.containsOnce(
+ document.body,
+ '.o_Message',
+ "should display a message component"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_Message_notificationIconClickable',
+ "should display the notification icon container"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_Message_notificationIcon',
+ "should display the notification icon"
+ );
+ assert.hasClass(
+ document.querySelector('.o_Message_notificationIcon'),
+ 'fa-paper-plane',
+ "icon should represent snailmail"
+ );
+
+ await afterNextRender(() => {
+ document.querySelector('.o_Message_notificationIconClickable').click();
+ });
+ assert.containsOnce(
+ document.body,
+ '.o_SnailmailErrorDialog',
+ "error dialog should be open"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_SnailmailErrorDialog_contentTrial',
+ "error dialog should have the 'trial' content"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_SnailmailErrorDialog_resendLetterButton',
+ "dialog should have a 'Re-send letter' button"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_SnailmailErrorDialog_cancelLetterButton',
+ "dialog should have a 'Cancel letter' button"
+ );
+
+ await afterNextRender(() => {
+ document.querySelector('.o_SnailmailErrorDialog_resendLetterButton').click();
+ });
+ assert.containsNone(
+ document.body,
+ '.o_SnailmailErrorDialog',
+ "dialog should be closed after click on 'Re-send letter'"
+ );
+ assert.verifySteps(
+ ['send_letter'],
+ "should have made a RPC call to 'send_letter'"
+ );
+});
+
+QUnit.test('Format Error', async function (assert) {
+ assert.expect(8);
+
+ const bus = new Bus();
+ bus.on('do-action', null, payload => {
+ assert.step('do_action');
+ assert.strictEqual(
+ payload.action,
+ 'snailmail.snailmail_letter_format_error_action',
+ "action should be the one for format error"
+ );
+ assert.strictEqual(
+ payload.options.additional_context.message_id,
+ 10,
+ "action should have correct message id"
+ );
+ });
+
+ await this.start({ env: { bus } });
+ const threadViewer = this.env.models['mail.thread_viewer'].create({
+ hasThreadView: true,
+ thread: [['create', {
+ id: 11,
+ model: 'mail.channel',
+ }]],
+ });
+ const message = this.env.models['mail.message'].create({
+ id: 10,
+ message_type: 'snailmail',
+ notifications: [['insert', {
+ failure_type: 'sn_format',
+ id: 11,
+ notification_status: 'exception',
+ notification_type: 'snail',
+ }]],
+ originThread: [['link', threadViewer.thread]],
+ });
+ await this.createMessageComponent(message, {
+ threadViewLocalId: threadViewer.threadView.localId
+ });
+
+ assert.containsOnce(
+ document.body,
+ '.o_Message',
+ "should display a message component"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_Message_notificationIconClickable',
+ "should display the notification icon container"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_Message_notificationIcon',
+ "should display the notification icon"
+ );
+ assert.hasClass(
+ document.querySelector('.o_Message_notificationIcon'),
+ 'fa-paper-plane',
+ "icon should represent snailmail"
+ );
+
+ await afterNextRender(() => {
+ document.querySelector('.o_Message_notificationIconClickable').click();
+ });
+ assert.verifySteps(
+ ['do_action'],
+ "should do an action to display the format error dialog"
+ );
+});
+
+QUnit.test('Missing Required Fields', async function (assert) {
+ assert.expect(8);
+
+ this.data['mail.message'].records.push({
+ id: 10, // random unique id, useful to link letter and notification
+ message_type: 'snailmail',
+ res_id: 20, // non 0 id, necessary to fetch failure at init
+ model: 'res.partner', // not mail.compose.message, necessary to fetch failure at init
+ });
+ this.data['mail.notification'].records.push({
+ failure_type: 'sn_fields',
+ mail_message_id: 10,
+ notification_status: 'exception',
+ notification_type: 'snail',
+ });
+ this.data['snailmail.letter'].records.push({
+ id: 22, // random unique id, will be asserted in the test
+ message_id: 10, // id of related message
+ });
+ const bus = new Bus();
+ bus.on('do-action', null, payload => {
+ assert.step('do_action');
+ assert.strictEqual(
+ payload.action,
+ 'snailmail.snailmail_letter_missing_required_fields_action',
+ "action should be the one for missing fields"
+ );
+ assert.strictEqual(
+ payload.options.additional_context.default_letter_id,
+ 22,
+ "action should have correct letter id"
+ );
+ });
+
+ await this.start({
+ env: { bus },
+ });
+ const threadViewer = this.env.models['mail.thread_viewer'].create({
+ hasThreadView: true,
+ thread: [['insert', { id: 20, model: 'res.partner' }]],
+ });
+ const message = this.env.models['mail.message'].findFromIdentifyingData({ id: 10 });
+ await this.createMessageComponent(message, {
+ threadViewLocalId: threadViewer.threadView.localId,
+ });
+
+ assert.containsOnce(
+ document.body,
+ '.o_Message',
+ "should display a message component"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_Message_notificationIconClickable',
+ "should display the notification icon container"
+ );
+ assert.containsOnce(
+ document.body,
+ '.o_Message_notificationIcon',
+ "should display the notification icon"
+ );
+ assert.hasClass(
+ document.querySelector('.o_Message_notificationIcon'),
+ 'fa-paper-plane',
+ "icon should represent snailmail"
+ );
+
+ await afterNextRender(() => {
+ document.querySelector('.o_Message_notificationIconClickable').click();
+ });
+ assert.verifySteps(
+ ['do_action'],
+ "an action should be done to display the missing fields dialog"
+ );
+});
+
+});
+});
+});
+
+});