text: const noop = function () {};
  const return_false = function () { return false; };
  const return_true = function () { return true; };
  
  set_global('document', {
      location: {}, // we need this to load compose.js
  });
  
  set_global('page_params', {
      use_websockets: false,
  });
  
  set_global('$', global.make_zjquery());
  
  set_global('compose_pm_pill', {
  });
  
  zrequire('people');
  zrequire('compose_ui');
  zrequire('compose');
  zrequire('util');
  zrequire('compose_state');
  zrequire('compose_actions');
  zrequire('stream_data');
  
  set_global('document', 'document-stub');
  
  const start = compose_actions.start;
  const cancel = compose_actions.cancel;
  const get_focus_area = compose_actions._get_focus_area;
  const respond_to_message = compose_actions.respond_to_message;
  const reply_with_mention = compose_actions.reply_with_mention;
  const quote_and_reply = compose_actions.quote_and_reply;
  
  const compose_state = global.compose_state;
  
  compose_state.recipient = (function () {
      let recipient;
  
      return function (arg) {
          if (arg === undefined) {
              return recipient;
          }
  
          recipient = arg;
      };
  }());
  
  set_global('reload_state', {
      is_in_progress: return_false,
  });
  
  set_global('notifications', {
      clear_compose_notifications: noop,
  });
  
  set_global('compose_fade', {
      clear_compose: noop,
  });
  
  set_global('drafts', {
      update_draft: noop,
  });
  
  set_global('resize', {
      resize_bottom_whitespace: noop,
  });
  
  set_global('narrow_state', {
      set_compose_defaults: noop,
  });
  
  set_global('unread_ops', {
      notify_server_message_read: noop,
  });
  
  set_global('common', {
      status_classes: 'status_classes',
  });
  
  function stub_selected_message(msg) {
      set_global('current_msg_list', {
          selected_message: function () {
              return msg;
          },
          can_mark_messages_read: function () {
              return true;
          },
      });
  }
  
  function stub_channel_get(success_value) {
      set_global('channel', {
          get: function (opts) {
              opts.success(success_value);
          },
      });
  }
  
  function assert_visible(sel) {
      assert($(sel).visible());
  }
  
  function assert_hidden(sel) {
      assert(!$(sel).visible());
  }
  
  run_test('initial_state', () => {
      assert.equal(compose_state.composing(), false);
      assert.equal(compose_state.get_message_type(), false);
      assert.equal(compose_state.has_message_content(), false);
  });
  
  run_test('start', () => {
      compose_actions.autosize_message_content = noop;
      compose_actions.expand_compose_box = noop;
      compose_actions.set_focus = noop;
      compose_actions.complete_starting_tasks = noop;
      compose_actions.blur_textarea = noop;
      compose_actions.clear_textarea = noop;
  
      // Start stream message
      global.narrow_state.set_compose_defaults = function () {
          const opts = {};
          opts.stream = 'stream1';
          opts.topic = 'topic1';
          return opts;
      };
  
      let opts = {};
      start('stream', opts);
  
      assert_visible('#stream-message');
      assert_hidden('#private-message');
  
      assert.equal($('#stream_message_recipient_stream').val(), 'stream1');
      assert.equal($('#stream_message_recipient_topic').val(), 'topic1');
      assert.equal(compose_state.get_message_type(), 'stream');
      assert(compose_state.composing());
  
      // Autofill stream field for single subscription
      const denmark = {
          subscribed: true,
          color: 'blue',
          name: 'Denmark',
          stream_id: 1,
      };
      stream_data.add_sub('Denmark', denmark);
  
      global.narrow_state.set_compose_defaults = function () {
          const opts = {};
          opts.trigger = "new topic button";
          return opts;
      };
  
      opts = {};
      start('stream', opts);
      assert.equal($('#stream_message_recipient_stream').val(), 'Denmark');
      assert.equal($('#stream_message_recipient_topic').val(), '');
  
      global.narrow_state.set_compose_defaults = function () {
          const opts = {};
          opts.trigger = "compose_hotkey";
          return opts;
      };
  
      opts = {};
      start('stream', opts);
      assert.equal($('#stream_message_recipient_stream').val(), 'Denmark');
      assert.equal($('#stream_message_recipient_topic').val(), '');
  
      const social = {
          subscribed: true,
          color: 'red',
          name: 'social',
          stream_id: 2,
      };
      stream_data.add_sub('social', social);
  
      // More than 1 subscription, do not autofill
      opts = {};
      start('stream', opts);
      assert.equal($('#stream_message_recipient_stream').val(), '');
      assert.equal($('#stream_message_recipient_topic').val(), '');
      stream_data.clear_subscriptions();
  
      // Start PM
      global.narrow_state.set_compose_defaults = function () {
          const opts = {};
          opts.private_message_recipient = 'foo@example.com';
          return opts;
      };
  
      opts = {
          content: 'hello',
      };
  
      $('#compose-textarea').trigger = noop;
      start('private', opts);
  
      assert_hidden('#stream-message');
      assert_visible('#private-message');
  
      assert.equal(compose_state.recipient(), 'foo@example.com');
      assert.equal($('#compose-textarea').val(), 'hello');
      assert.equal(compose_state.get_message_type(), 'private');
      assert(compose_state.composing());
  
      // Cancel compose.
      let pill_cleared;
  
      compose_pm_pill.clear = function () {
          pill_cleared = true;
      };
  
      assert_hidden('#compose_controls');
      cancel();
      assert(pill_cleared);
      assert_visible('#compose_controls');
      assert_hidden('#private-message');
      assert(!compose_state.composing());
  });
  
  run_test('respond_to_message', () => {
      // Test PM
      const person = {
          user_id: 22,
          email: 'alice@example.com',
          full_name: 'Alice',
      };
      people.add_in_realm(person);
  
      let msg = {
          type: 'private',
          sender_id: person.user_id,
      };
      stub_selected_message(msg);
  
      let opts = {
          reply_type: 'personal',
      };
  
      respond_to_message(opts);
      assert.equal(compose_state.recipient(), 'alice@example.com');
  
      // Test stream
      msg = {
          type: 'stream',
          stream: 'devel',
          topic: 'python',
          reply_to: 'bob', // compose.start needs this for dubious reasons
      };
      stub_selected_message(msg);
  
      opts = {
      };
  
      respond_to_message(opts);
      assert.equal($('#stream_message_recipient_stream').val(), 'devel');
  });
  
  run_test('reply_with_mention', () => {
      const msg = {
          type: 'stream',
          stream: 'devel',
          topic: 'python',
          reply_to: 'bob', // compose.start needs this for dubious reasons
          sender_full_name: 'Bob Roberts',
          sender_id: 40,
      };
      stub_selected_message(msg);
  
      let syntax_to_insert;
      compose_ui.insert_syntax_and_focus = function (syntax) {
          syntax_to_insert = syntax;
      };
  
      const opts = {
      };
  
      reply_with_mention(opts);
      assert.equal($('#stream_message_recipient_stream').val(), 'devel');
      assert.equal(syntax_to_insert, '@**Bob Roberts**');
      assert(compose_state.has_message_content());
  
      // Test for extended mention syntax
      const bob_1 = {
          user_id: 30,
          email: 'bob1@example.com',
          full_name: 'Bob Roberts',
      };
      people.add_in_realm(bob_1);
      const bob_2 = {
          user_id: 40,
          email: 'bob2@example.com',
          full_name: 'Bob Roberts',
      };
      people.add_in_realm(bob_2);
  
      reply_with_mention(opts);
      assert.equal($('#stream_message_recipient_stream').val(), 'devel');
      assert.equal(syntax_to_insert, '@**Bob Roberts|40**');
      assert(compose_state.has_message_content());
  });
  
  run_test('quote_and_reply', () => {
      const msg = {
          type: 'stream',
          stream: 'devel',
          topic: 'python',
          reply_to: 'bob',
          sender_full_name: 'Bob Roberts',
          sender_id: 40,
      };
      stub_selected_message(msg);
      stub_channel_get({ raw_content: 'Testing.' });
  
      current_msg_list.selected_id = function () {
          return 100;
      };
  
      compose_ui.insert_syntax_and_focus = function (syntax) {
          assert.equal(syntax, '[Quoting…]\n');
      };
  
      compose_ui.replace_syntax = function (syntax, replacement) {
          assert.equal(syntax, '[Quoting…]');
          assert.equal(replacement, '```quote\nTesting.\n```');
      };
  
      const opts = {
          reply_type: 'personal',
      };
  
      $('#compose-textarea').caret = (pos) => {
          assert.equal(pos, 0);
      };
  
      quote_and_reply(opts);
  
      current_msg_list.selected_message = function () {
          return {
              type: 'stream',
              stream: 'devel',
              topic: 'test',
              reply_to: 'bob',
              sender_full_name: 'Bob',
              sender_id: 40,
              raw_content: 'Testing.',
          };
      };
  
      channel.get = function () {
          assert.fail('channel.get should not be used if raw_content is present');
      };
  
      quote_and_reply(opts);
  });
  
  run_test('get_focus_area', () => {
      assert.equal(get_focus_area('private', {}), '#private_message_recipient');
      assert.equal(get_focus_area('private', {
          private_message_recipient: 'bob@example.com'}), '#compose-textarea');
      assert.equal(get_focus_area('stream', {}), '#stream_message_recipient_stream');
      assert.equal(get_focus_area('stream', {stream: 'fun'}),
                   '#stream_message_recipient_topic');
      assert.equal(get_focus_area('stream', {stream: 'fun',
                                             topic: 'more'}),
                   '#compose-textarea');
      assert.equal(get_focus_area('stream', {stream: 'fun',
                                             topic: 'more',
                                             trigger: 'new topic button'}),
                   '#stream_message_recipient_topic');
  });
  
  run_test('focus_in_empty_compose', () => {
      $('#compose-textarea').is = function (attr) {
          assert.equal(attr, ':focus');
          return $('#compose-textarea').is_focused;
      };
  
      compose_state.composing = return_true;
      $('#compose-textarea').val('');
      $('#compose-textarea').focus();
      assert(compose_state.focus_in_empty_compose());
  
      compose_state.composing = return_false;
      assert(!compose_state.focus_in_empty_compose());
  
      $('#compose-textarea').val('foo');
      assert(!compose_state.focus_in_empty_compose());
  
      $('#compose-textarea').blur();
      assert(!compose_state.focus_in_empty_compose());
  });
  
  run_test('on_narrow', () => {
      let cancel_called = false;
      compose_actions.cancel = function () {
          cancel_called = true;
      };
      compose_actions.on_narrow({
          force_close: true,
      });
      assert(cancel_called);
  
      let on_topic_narrow_called = false;
      compose_actions.on_topic_narrow = function () {
          on_topic_narrow_called = true;
      };
      narrow_state.narrowed_by_topic_reply = function () {
          return true;
      };
      compose_actions.on_narrow({
          force_close: false,
      });
      assert(on_topic_narrow_called);
  
      let update_message_list_called = false;
      narrow_state.narrowed_by_topic_reply = function () {
          return false;
      };
      compose_fade.update_message_list = function () {
          update_message_list_called = true;
      };
      compose_state.has_message_content = function () {
          return true;
      };
      compose_actions.on_narrow({
          force_close: false,
      });
      assert(update_message_list_called);
  
      compose_state.has_message_content = function () {
          return false;
      };
      let start_called = false;
      compose_actions.start = function () {
          start_called = true;
      };
      narrow_state.narrowed_by_pm_reply = function () {
          return true;
      };
      compose_actions.on_narrow({
          force_close: false,
          trigger: 'not-search',
          private_message_recipient: 'not@empty.com',
      });
      assert(start_called);
  
      start_called = false;
      compose_actions.on_narrow({
          force_close: false,
          trigger: 'search',
          private_message_recipient: '',
      });
      assert(!start_called);
  
      narrow_state.narrowed_by_pm_reply = function () {
          return false;
      };
      cancel_called = false;
      compose_actions.on_narrow({
          force_close: false,
      });
      assert(cancel_called);
  });
  