| asynctest(
  'Browser Test: ui.SerialisedLinkTest',
  [
    'ephox.agar.api.ApproxStructure',
    'ephox.agar.api.Assertions',
    'ephox.agar.api.Chain',
    'ephox.agar.api.FocusTools',
    'ephox.agar.api.GeneralSteps',
    'ephox.agar.api.Keyboard',
    'ephox.agar.api.Keys',
    'ephox.agar.api.Logger',
    'ephox.agar.api.Mouse',
    'ephox.agar.api.Pipeline',
    'ephox.agar.api.Step',
    'ephox.agar.api.UiFinder',
    'ephox.alloy.api.system.Attachment',
    'ephox.alloy.test.GuiSetup',
    'ephox.boulder.api.FieldSchema',
    'ephox.boulder.api.ValueSchema',
    'ephox.katamari.api.Fun',
    'ephox.sand.api.PlatformDetection',
    'ephox.sugar.api.dom.Focus',
    'ephox.sugar.api.node.Body',
    'ephox.sugar.api.node.Element',
    'ephox.sugar.api.properties.Class',
    'ephox.sugar.api.search.Traverse',
    'global!navigator',
    'tinymce.themes.mobile.test.ui.TestEditor',
    'tinymce.themes.mobile.test.ui.TestSelectors',
    'tinymce.themes.mobile.test.ui.TestStyles',
    'tinymce.themes.mobile.test.ui.TestUi',
    'tinymce.themes.mobile.ui.IosRealm',
    'tinymce.themes.mobile.ui.LinkButton'
  ],
  function (
    ApproxStructure, Assertions, Chain, FocusTools, GeneralSteps, Keyboard, Keys, Logger, Mouse, Pipeline, Step, UiFinder, Attachment, GuiSetup, FieldSchema,
    ValueSchema, Fun, PlatformDetection, Focus, Body, Element, Class, Traverse, navigator, TestEditor, TestSelectors, TestStyles, TestUi, IosRealm, LinkButton
  ) {
    var success = arguments[arguments.length - 2];
    var failure = arguments[arguments.length - 1];
    var detection = PlatformDetection.detect();
    
    var realm = IosRealm();
    // Make toolbar appear
    Class.add(realm.system().element(), 'tinymce-mobile-fullscreen-maximized');
    var body = Body.body();
    Attachment.attachSystem(body, realm.system());
    var doc = Traverse.owner(body);
    TestStyles.addStyles();
    
    var unload = function () {
      TestStyles.removeStyles();
      Attachment.detachSystem(realm.system());
    };
    var tEditor = TestEditor();
    realm.setToolbarGroups([
      {
        label: 'group1',
        items: [
          LinkButton.sketch(realm, tEditor.editor())
        ]
      }
    ]);
    var sAssertNavigation = function (label, prevEnabled, nextEnabled) {
      return Logger.t(
        label,
        Step.sync(function () {
          var active = Focus.active().getOrDie();
          // The buttons are next and previous siblings
          var prev = Traverse.parent(active).bind(Traverse.prevSibling).getOrDie('Could not find button to left');
          var next = Traverse.parent(active).bind(Traverse.nextSibling).getOrDie('Could not find button to right');
          var assertNavButton = function (buttonLabel, expected, button) {
            Assertions.assertStructure(
              'Checking ' + buttonLabel + ' button should be enabled = ' + expected,
              ApproxStructure.build(function (s, str, arr) {
                return s.element('span', {
                  attr: {
                    role: str.is('button')
                  },
                  classes: [
                    (expected ? arr.not : arr.has)('tinymce-mobile-toolbar-navigation-disabled')
                  ]
                });
              }),
              button
            );
          };
          assertNavButton('previous', prevEnabled, prev);
          assertNavButton('next', nextEnabled, next);
        })
      );
    };
    var sClickNavigation = function (selector) {
      return Chain.asStep({ }, [
        TestUi.cGetFocused,
        TestUi.cGetParent,
        TestUi.cGetParent,
        UiFinder.cFindIn(selector),
        Mouse.cClick
      ]);
    };
    var sClickPrev = sClickNavigation('.tinymce-mobile-icon-previous');
    var sClickNext = sClickNavigation('.tinymce-mobile-icon-next');
    var sAssertUrlFocused = GeneralSteps.sequence([
      FocusTools.sTryOnSelector('Focus should be on input with link URL', doc, 'input[placeholder="Type or paste URL"]'),
      sAssertNavigation('Checking initial navigation on text node', false, true)
    ]);
    var sAssertTextFocused = GeneralSteps.sequence([
      FocusTools.sTryOnSelector('Focus should be on input with link text', doc, 'input[placeholder="Link text"]'),
      sAssertNavigation('Checking navigation for link text', true, true)
    ]);
    var sAssertTitleFocused = GeneralSteps.sequence([
      FocusTools.sTryOnSelector('Focus should be on input with link title', doc, 'input[placeholder="Link title"]'),
      sAssertNavigation('Checking navigation for link text', true, true)
    ]);
    var sAssertTargetFocused = GeneralSteps.sequence([
      FocusTools.sTryOnSelector('Focus should be on input with link target', doc, 'input[placeholder="Link target"]'),
      sAssertNavigation('Checking navigation for link target', true, false)
    ]);
    var sTestNavigation = GeneralSteps.sequence([
      Keyboard.sKeydown(doc, Keys.tab(), { }),
      sAssertTextFocused,
      Keyboard.sKeydown(doc, Keys.tab(), { }),
      sAssertTitleFocused,
      Keyboard.sKeydown(doc, Keys.tab(), { }),
      sAssertTargetFocused,
      Keyboard.sKeydown(doc, Keys.tab(), { shift: true }),
      sAssertTitleFocused,
      Keyboard.sKeydown(doc, Keys.tab(), { shift: false }),
      sAssertTargetFocused,
      Keyboard.sKeydown(doc, Keys.tab(), { }),
      Step.wait(1000),
      Logger.t('Checking pressing tab at the end should not move focus', sAssertTargetFocused),
      sClickPrev,
      sAssertTitleFocused,
      sClickNext,
      sAssertTargetFocused,
      sClickPrev,
      sAssertTitleFocused,
      sClickPrev,
      sAssertTextFocused,
      sClickPrev,
      sAssertUrlFocused
    ]);
    var sClickLink = Mouse.sClickOn(realm.element(), TestSelectors.link());
    var sTestScenario = function (rawScenario) {
      var scenario = ValueSchema.asRawOrDie('Checking scenario', ValueSchema.objOf([
        FieldSchema.strict('label'),
        FieldSchema.defaulted('content', ''),
        FieldSchema.defaulted('node', Element.fromText('')),
        FieldSchema.strictObjOf('fields', [
          FieldSchema.option('url'),
          FieldSchema.option('text'),
          FieldSchema.option('title'),
          FieldSchema.option('target')
        ]),
        FieldSchema.strict('expected'),
        FieldSchema.defaulted('beforeExecute', Step.pass),
        FieldSchema.defaulted('mutations', Fun.constant(Step.pass))
      ]), rawScenario);
      return Logger.t(
        scenario.label,
        GeneralSteps.sequence([
          tEditor.sPrepareState(scenario.node.dom(), scenario.content),
          sClickLink,
          TestUi.sSetFieldOptValue(scenario.fields.url),
          sClickNext,
          sAssertTextFocused,
          TestUi.sSetFieldOptValue(scenario.fields.text),
          sClickNext,
          sAssertTitleFocused,
          TestUi.sSetFieldOptValue(scenario.fields.title),
          sClickNext,
          sAssertTargetFocused,
          TestUi.sSetFieldOptValue(scenario.fields.target),
          sClickPrev,
          sAssertTitleFocused,
          sClickPrev,
          sAssertTextFocused,
          sClickPrev,
          sAssertUrlFocused,
          scenario.beforeExecute,
          Keyboard.sKeydown(doc, Keys.enter(), { }),
          tEditor.sAssertEq('Checking insert content', scenario.expected),
          scenario.mutations(scenario.node),
          tEditor.sClear
        ])
      );
    };
    Pipeline.async({}, detection.browser.isChrome() ? [
      GuiSetup.mAddStyles(doc, [
        '.tinymce-mobile-icon-link:before { content: "LINK"; background: black; color: white; }',
        // Speeds up tests.
        '.tinymce-mobile-serialised-dialog-chain { transition: left linear 0.000001s !important }'
      ]),
      TestStyles.sWaitForToolstrip(realm),
      tEditor.sPrepareState(Element.fromText('hi'), 'link-text'),
      sClickLink,
      FocusTools.sTryOnSelector('Focus should be on input with link URL', doc, 'input[placeholder="Type or paste URL"]'),
      sAssertNavigation('Checking initial navigation on text node', false, true),
      sTestNavigation,
      Step.sync(function () {
        realm.restoreToolbar();
      }),
      sTestScenario({
        label: 'Testing hitting ENTER after just setting URL',
        fields: {
          url: 'http://fake-url'
        },
        expected: [
          {
            method: 'insertContent',
            data: {
              tag: 'a',
              attributes: {
                href: 'http://fake-url'
              },
              innerText: 'http://fake-url'
            }
          }
        ]
      }),
      sTestScenario({
        label: 'Testing hitting ENTER after filling in URL and text',
        fields: {
          url: 'http://fake-url',
          text: 'LinkText'
        },
        expected: [
          {
            method: 'insertContent',
            data: {
              tag: 'a',
              attributes: {
                href: 'http://fake-url'
              },
              innerText: 'LinkText'
            }
          }
        ]
      }),
      sTestScenario({
        label: 'Testing hitting ENTER after filling in URL and title (not text)',
        fields: {
          url: 'http://fake-url',
          title: 'Title'
        },
        expected: [
          {
            method: 'insertContent',
            data: {
              tag: 'a',
              attributes: {
                href: 'http://fake-url',
                title: 'Title'
              },
              innerText: 'http://fake-url'
            }
          }
        ]
      }),
      sTestScenario({
        label: 'Testing hitting ENTER after filling in URL, text, and title',
        fields: {
          url: 'http://fake-url',
          text: 'LinkText',
          title: 'Title'
        },
        expected: [
          {
            method: 'insertContent',
            data: {
              tag: 'a',
              attributes: {
                href: 'http://fake-url',
                title: 'Title'
              },
              innerText: 'LinkText'
            }
          }
        ]
      }),
      sTestScenario({
        label: 'Testing hitting ENTER after filling in URL, text, title, and target',
        fields: {
          url: 'http://fake-url',
          text: 'LinkText',
          title: 'Title',
          target: 'Target'
        },
        expected: [
          {
            method: 'insertContent',
            data: {
              tag: 'a',
              attributes: {
                href: 'http://fake-url',
                title: 'Title',
                target: 'Target'
              },
              innerText: 'LinkText'
            }
          }
        ]
      }),
      sTestScenario({
        label: 'Testing hitting ENTER after filling in URL with initial text selection',
        content: 'Initial text selection',
        fields: {
          url: 'http://fake-url'
        },
        expected: [
          {
            method: 'insertContent',
            data: {
              tag: 'a',
              attributes: {
                href: 'http://fake-url'
              },
              innerText: 'Initial text selection'
            }
          }
        ]
      }),
      sTestScenario({
        label: 'Testing hitting ENTER after filling in nothing with an existing link with url',
        node: Element.fromHtml('<a href="http://prepared-url">Prepared</a>'),
        fields: { },
        expected: [ ],
        mutations: function (node) {
          return Assertions.sAssertStructure('Checking mutated structure', ApproxStructure.build(function (s, str, arr) {
            return s.element('a', {
              attrs: {
                href: str.is('http://prepared-url')
              },
              html: str.is('Prepared')
            });
          }), node);
        }
      }),
      sTestScenario({
        label: 'Testing hitting ENTER after filling in URL with an existing link with url (and text content did not match URL previously)',
        node: Element.fromHtml('<a href="http://prepared-url">Prepared</a>'),
        fields: {
          url: 'http://new-url'
        },
        expected: [ ],
        mutations: function (node) {
          return Assertions.sAssertStructure('Checking mutated structure', ApproxStructure.build(function (s, str, arr) {
            return s.element('a', {
              attrs: {
                href: str.is('http://new-url')
              },
              html: str.is('Prepared')
            });
          }), node);
        }
      }),
      sTestScenario({
        label: 'Testing hitting ENTER after filling in URL with an existing link with url (and text content matched URL previously)',
        node: Element.fromHtml('<a href="http://prepared-url">http://prepared-url</a>'),
        fields: {
          url: 'http://new-url'
        },
        expected: [ ],
        mutations: function (node) {
          return Assertions.sAssertStructure('Checking mutated structure', ApproxStructure.build(function (s, str, arr) {
            return s.element('a', {
              attrs: {
                href: str.is('http://new-url')
              },
              html: str.is('http://new-url')
            });
          }), node);
        }
      }),
      sTestScenario({
        label: 'Testing hitting ENTER after filling in URL and text with an existing link with url',
        node: Element.fromHtml('<a href="http://prepared-url">any text</a>'),
        fields: {
          url: 'http://new-url',
          text: 'new-text'
        },
        expected: [ ],
        mutations: function (node) {
          return Assertions.sAssertStructure('Checking mutated structure', ApproxStructure.build(function (s, str, arr) {
            return s.element('a', {
              attrs: {
                href: str.is('http://new-url')
              },
              html: str.is('new-text')
            });
          }), node);
        }
      })
    ] : [], function () {
      unload(); success();
    }, failure);
  }
);
 |