Merge "autotest:  Reference count the start/stop servod calls." into main
diff --git a/src/moblab-ui/e2e/run-suite/CTS/select-form.e2e-spec.ts b/src/moblab-ui/e2e/run-suite/CTS/select-form.e2e-spec.ts
new file mode 100644
index 0000000..effd946
--- /dev/null
+++ b/src/moblab-ui/e2e/run-suite/CTS/select-form.e2e-spec.ts
@@ -0,0 +1,106 @@
+// Copyright 2021 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {browser, ExpectedConditions as until} from 'protractor';
+import {SelectForm, RunSuitePage} from '../run-suite.po';
+
+describe('Navigate to Moblab run-suite page, as TC No.48', () => {
+  const page = new RunSuitePage(),
+    form = new SelectForm();
+
+  beforeAll(() => {
+    browser.waitForAngularEnabled(false);
+    page.navigateTo();
+  });
+
+  it('has CTS form selected by default', async () => {
+    expect(await page.suiteActive().getText()).toEqual('CTS');
+  });
+
+  describe('then select build target, as TC No.49', () => {
+    beforeAll(() => {
+      browser.wait(until.invisibilityOf(page.loadingMessage()));
+      form.modelButton().click();
+      form.dropDownMenu(0).click();
+      browser.wait(until.invisibilityOf(page.loadingMessage()));
+    });
+
+    it('"Please select build target" placeholder should disappear', () => {
+      expect(
+        until.invisibilityOf(form.placeholderOnSelectMilestone())
+      ).toBeTruthy();
+    });
+
+    describe('then select milestone, as TC No.50', () => {
+      beforeAll(() => {
+        form.milestoneButton().click();
+        form.dropDownMenu(0).click();
+        browser.wait(until.invisibilityOf(page.loadingMessage()));
+      });
+
+      it('"Please select milestone" placeholder should disappear', () => {
+        expect(
+          until.invisibilityOf(form.placeholderOnSelectBuild())
+        ).toBeTruthy();
+      });
+
+      describe('then select build version, as TC No.51', () => {
+        beforeAll(() => {
+          form.buildButton().click();
+          form.dropDownMenu(0).click();
+          browser.wait(until.invisibilityOf(page.loadingMessage()));
+        });
+
+        it('"Please select build" placeholder should disappear', () => {
+          expect(until.invisibilityOf(form.placeholderOnPool())).toBeTruthy();
+        });
+
+        describe('then pick cts from Cts version dropdown options, as TC No.53', () => {
+          beforeAll(() => {
+            form.selectCTSVersion().click();
+            form.dropDownMenu(0).click();
+            browser.wait(
+              until.invisibilityOf(form.placeholderOnSelectCTSVersion())
+            );
+          });
+
+          it('has cts populates field', async () => {
+            expect(await form.selectCTSVersion().getText()).toEqual('cts');
+          });
+
+          describe('then pick cts_hardware from Cts version dropdown options, as TC No.378', () => {
+            beforeAll(() => {
+              form.selectCTSVersion().click();
+              form.dropDownMenu(1).click();
+              browser.wait(
+                until.invisibilityOf(form.placeholderOnSelectCTSVersion())
+              );
+            });
+
+            it('has cts_hardware populates field', async () => {
+              expect(await form.selectCTSVersion().getText()).toEqual(
+                'cts_hardware'
+              );
+            });
+
+            describe('then input specific CTS Modules, as TC No.54', () => {
+              beforeAll(() => {
+                form.textModeule().click();
+                form.textModeule().sendKeys('arm.CtsEffectTestCases');
+              });
+
+              it('has input CTS Modules in the Modules field', () => {
+                expect(form.textCloseButton().isPresent()).toBeTruthy();
+              });
+
+              it('has Run Suite button clickable', () => {
+                expect(form.runSuiteButton().isEnabled()).toBeTruthy();
+              });
+            });
+          });
+        });
+      });
+    });
+  });
+});
diff --git a/src/moblab-ui/e2e/run-suite/run-suite.po.ts b/src/moblab-ui/e2e/run-suite/run-suite.po.ts
new file mode 100644
index 0000000..aec3f42
--- /dev/null
+++ b/src/moblab-ui/e2e/run-suite/run-suite.po.ts
@@ -0,0 +1,87 @@
+// Copyright 2021 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import {browser, by, element} from 'protractor';
+export class SelectForm {
+  modelButton() {
+    return element(by.id('modelSelector'));
+  }
+
+  milestoneButton() {
+    return element(by.id('milestoneSelector'));
+  }
+
+  buildButton() {
+    return element(by.id('buildSelector'));
+  }
+
+  poolButton() {
+    return element(by.id('poolSelector'));
+  }
+
+  placeholderOnPool() {
+    return element(by.id('mat-hint-1'));
+  }
+
+  placeholderOnBuildTarget() {
+    return element(by.id('mat-hint-2'));
+  }
+
+  placeholderOnSelectMilestone() {
+    return element(by.id('mat-hint-3'));
+  }
+
+  placeholderOnSelectBuild() {
+    return element(by.id('mat-hint-4'));
+  }
+
+  /**
+   * Returns the selected index from dropdown menu which exclude "grey out/failed" option.
+   * @param {number} index - the index of dropdown menu start from "0".
+   */
+  dropDownMenu(index: number) {
+    return element.all(by.css('[role="listbox"] [tabindex="0"]')).get(index);
+  }
+
+  runSuiteButton() {
+    return element(by.tagName('app-run-suite-button'));
+  }
+
+  selectCTSVersion() {
+    return element(by.className('mat-select-value'));
+  }
+
+  placeholderOnSelectCTSVersion() {
+    return element(by.className('mat-select-placeholder'));
+  }
+
+  textModeule() {
+    return element(by.id('textInput'));
+  }
+
+  textCloseButton() {
+    return element(by.buttonText('close'));
+  }
+}
+
+export class RunSuitePage {
+  runSuiteMenu() {
+    return element(by.css('a[href*="#/run_tests"]'));
+  }
+
+  /**
+   * Returns the current using suite
+   */
+  suiteActive() {
+    return element(by.css('[role="tablist"] [tabindex="0"]'));
+  }
+
+  loadingMessage() {
+    return element(by.id('loading-message'));
+  }
+
+  navigateTo() {
+    return browser.get('/#/run_tests');
+  }
+}