Skip to content

Commit

Permalink
QA tests for the new corgi UI (#523)
Browse files Browse the repository at this point in the history
* try to use current ubuntu image in CircleCI
* use no terminal in CircleCI tests for poetry
see
python-poetry/poetry#7184
for details of the issue.

* added testrail case numbers and parametrize
* removed all mark.smoke marker - not needed
* added more testrail case numbers
Co-authored-by: therealmarv <1050582+therealmarv@users.noreply.github.com>
  • Loading branch information
omehes authored and TylerZeroMaster committed Feb 24, 2023
1 parent 7ee1d06 commit 053774a
Show file tree
Hide file tree
Showing 18 changed files with 1,034 additions and 743 deletions.
50 changes: 49 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,54 @@ orbs:
shellcheck: circleci/shellcheck@1.3.16
python: circleci/python@2.0.3
jobs:
integration-test:
machine:
image: ubuntu-2004:current
docker_layer_caching: true

environment:
TEST_RESULTS_DIR: /tmp/test-results
TEST_RESULTS_INTEGRATION: /tmp/test-results/junit-integration.xml
TEST_RESULTS_UI: /tmp/test-results/junit-ui.xml
STACK_NAME: dev
TAG: dev
DOMAIN: http://localhost

steps:
- checkout
# Permissions errors occurred with 0775 due to
# docker user not having permissions over this folder.
- run: mkdir -p -m=0777 $TEST_RESULTS_DIR
- run:
name: Install poetry
command: |
curl -sSL https://install.python-poetry.org | python3 -
echo 'export PATH=$HOME/.local/bin:$PATH' >> $BASH_ENV
- run:
name: Install dependencies + playwright
command: |
cd ./backend/app
poetry install --no-root --no-interaction --no-ansi
poetry run playwright install chromium --with-deps
- run:
name: Run integration and ui tests
command: |
./scripts/tests.ci.sh
REVISION=$(git --git-dir=./.git rev-parse --short HEAD)
cd ./backend/app
REVISION=$REVISION poetry run pytest -vvv --junitxml="${TEST_RESULTS_UI}" --base-url="${DOMAIN}" ./tests/ui
REVISION=$REVISION poetry run pytest -vvv --junitxml="${TEST_RESULTS_INTEGRATION}" --base-url="${DOMAIN}" ./tests/integration
- store_artifacts:
path: /tmp/test-results/junit-integration.xml
destination: test-reports

- store_artifacts:
path: /tmp/test-results/junit-ui.xml
destination: test-reports

- store_test_results:
path: /tmp/test-results

frontend-unit:
docker:
Expand Down Expand Up @@ -35,7 +83,7 @@ jobs:
cd ./backend/app
poetry install --no-root --no-interaction --no-ansi
poetry run pytest -vvv --junitxml=/tmp/test-reports/junit.xml ./tests/unit
- store_artifacts:
path: /tmp/test-reports/junit.xml
destination: test-reports
Expand Down
23 changes: 21 additions & 2 deletions backend/app/tests/ui/fixtures/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,27 @@ def chrome_page():
"""Return playwright chromium browser page"""
playwright_sync = sync_playwright().start()
chrome_browser = playwright_sync.chromium.launch(
headless=True, slow_mo=750, timeout=99999
headless=True, slow_mo=800, timeout=120000
)
page = chrome_browser.new_page()
context = chrome_browser.new_context(storage_state="state.json")

page = context.new_page()
yield page

chrome_browser.close()
playwright_sync.stop()


@pytest.fixture
def chrome_page_slow():
"""Return playwright chromium browser page - slow flow"""
playwright_sync = sync_playwright().start()
chrome_browser = playwright_sync.chromium.launch(
headless=True, slow_mo=3700, timeout=120000
)
context = chrome_browser.new_context(storage_state="state.json")

page = context.new_page()
yield page

chrome_browser.close()
Expand All @@ -19,4 +37,5 @@ def chrome_page():
@pytest.fixture
def corgi_base_url(base_url):
"""Return local corgi url"""
base_url = "https://corgi-514.ce.openstax.org/"
return base_url
216 changes: 113 additions & 103 deletions backend/app/tests/ui/pages/home.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,164 +3,174 @@ def __init__(self, page):
self.page = page

@property
def create_new_job_button_is_visible(self):
return self.page.wait_for_selector("button.create-job-button :text('Create a new job')")
def book_input_fields(self):
return self.page.locator("child(2) > div > div:nth-child(2)")

def click_create_new_job_button(self):
self.create_new_job_button_is_visible.click()
@property
def job_types_check_boxes(self):
return self.page.locator("child(2) > div > div:nth-child(3)")

@property
def create_job_modal_is_open(self):
return self.page.locator("job-modal")
def jobs_data_table(self):
return self.page.locator("div.mdc-data-table")

@property
def create_button_is_visible(self):
return self.page.wait_for_selector("button.create-button-start-job :text('Create')")
def jobs_pagination_box(self):
return self.page.locator("div.mdc-data-table__pagination")

def click_create_button(self):
self.create_button_is_visible.click()
@property
def repo_field(self):
return self.page.wait_for_selector("id=repo-input")

@property
def modal_cancel_button_is_visible(self):
return self.page.locator("button.job-cancel-button")
def repo_field_input_locator(self):
return self.page.locator('#repo-input input')

def click_modal_cancel_button(self):
self.modal_cancel_button_is_visible.click()
def fill_repo_field(self, value):
self.repo_field_input_locator.fill(value)

@property
def pdf_radio_button(self):
return self.page.locator("div.v-radio.pdf-radio-button")
def click_repo_field(self):
self.repo_field_input_locator.click()

def click_pdf_radio_button(self):
self.pdf_radio_button.click()
@property
def book_field(self):
return self.page.wait_for_selector("id=book-input")

@property
def web_preview_radio_button(self):
return self.page.locator("div.v-radio.preview-radio-button")
def book_field_input_locator(self):
return self.page.locator('#book-input input')

def fill_book_field(self, value):
self.book_field_input_locator.fill(value)

def click_web_preview_radio_button(self):
self.web_preview_radio_button.click()
def click_book_field(self):
self.book_field_input_locator.click()

@property
def pdf_git_radio_button(self):
return self.page.locator("div.v-radio.git-pdf-radio-button")
def version_field(self):
return self.page.wait_for_selector("id=version-input")

def click_pdf_git_radio_button(self):
self.pdf_git_radio_button.click()
@property
def version_field_input_locator(self):
return self.page.locator('#version-input input')

def fill_version_field(self, value):
self.version_field_input_locator.fill(value)

def click_version_field(self):
self.version_field_input_locator.click()

@property
def web_preview_git_radio_button(self):
return self.page.locator("div.v-radio.git-preview-radio-button")
def pdf_job_option(self):
return self.page.wait_for_selector("id=PDF-job-option")

def click_web_preview_git_radio_button(self):
self.web_preview_git_radio_button.click()
def click_pdf_job_option(self):
self.pdf_job_option.click()

@property
def modal_hint_text(self):
return self.page.locator(
"div.v-dialog__content.v-dialog__content--active > div > div > div.v-card__text"
)
def webview_job_option(self):
return self.page.wait_for_selector("id=Web-job-option")

def click_webview_job_option(self):
self.webview_job_option.click()

@property
def collection_id_field_texts(self):
return self.page.locator(
"div:nth-child(2) > div:nth-child(1) > div > div > div.v-text-field__details"
)
def epub_job_option(self):
return self.page.wait_for_selector("id=EPUB-job-option")

def click_epub_job_option(self):
self.epub_job_option.click()

@property
def version_field_texts(self):
return self.page.locator(
"div:nth-child(2) > div:nth-child(2) > div > div > div.v-text-field__details"
)
def docx_job_option(self):
return self.page.wait_for_selector("id=Docx-job-option")

def click_docx_job_option(self):
self.docx_job_option.click()

@property
def style_field_texts(self):
return self.page.locator(
"div:nth-child(2) > div:nth-child(3) > div > div > div.v-text-field__details"
)
def create_new_job_button_is_enabled(self):
return self.page.is_enabled("id=submit-job-button")

@property
def content_server_field_texts(self):
return self.page.locator(
"div:nth-child(2) > div:nth-child(4) > div > div > div.v-text-field__details"
)
def create_new_job_button_locator(self):
return self.page.locator("id=submit-job-button")

def click_create_new_job_button(self):
self.create_new_job_button_locator.click()

@property
def collection_id_field(self):
return self.page.locator(
"div:nth-child(2) > div:nth-child(1) > div > div > div.v-input__slot > div > label"
)
def error_message_dialog_locator(self):
return self.page.locator("div.error.mdc-banner")

@property
def colid_value(self):
return self.page.locator(
"div:nth-child(3) > div > div > table > tbody > tr:nth-child(1) > td:nth-child(4)"
)
def error_banner_okay_button_locator(self):
return self.page.locator("div.error.mdc-banner :text('Okay')")

def fill_collection_id_field(self, value):
self.collection_id_field.fill(value)
def click_error_banner_okay_button(self):
self.error_banner_okay_button_locator.click()

@property
def version_field(self):
return self.page.locator(
"div:nth-child(2) > div:nth-child(2) > div > div > div.v-input__slot > div > label"
)
def error_banner_is_visible(self):
return self.page.is_visible("div.error.mdc-banner")

def fill_version_field(self, value):
self.version_field.fill(value)
@property
def repo_dropdown_is_visible(self):
return self.page.locator("#repo-input input")

@property
def style_field(self):
return self.page.locator(
"div:nth-child(2) > div:nth-child(3) > div > div > div.v-input__slot > div.v-select__slot > label"
)
def book_dropdown_is_visible(self):
return self.page.locator("#book-input input")

@property
def style_field_drop(self):
return self.page.locator(
"div:nth-child(2) > div:nth-child(3) > div > div > div.v-input__slot > div.v-select__slot"
)
def version_dropdown_is_visible(self):
return self.page.locator("#version-input input")

def fill_style_field(self, value):
self.style_field.fill(value)
@property
def job_id(self):
return self.page.locator("td.mdc-data-table__cell--numeric >> nth=0")

def click_style_field(self):
self.style_field_drop.click()
def click_job_id(self):
self.job_id.click()

@property
def style_field_dropdown(self):
return self.page.locator("#list-item-209-0 > div > div")
def latest_job_status(self):
return self.page.locator("td:nth-child(6) > img >> nth=0").get_attribute('alt')

@property
def server_field(self):
return self.page.locator(
"div:nth-child(2) > div:nth-child(4) > div > div > div.v-input__slot > div.v-select__slot > label"
)
def elapsed_time(self):
return self.page.locator("td:nth-child(7) >> nth=0")

def fill_server_field(self, value):
self.server_field.fill(value)
@property
def wait_for_elapsed_time(self):
return self.page.locator("td:nth-child(7) >> nth=0")

@property
def queued_repo_name(self):
return self.page.locator("tr:nth-child(1) > td:nth-child(3)")

@property
def content_server_locator(self):
return self.page.locator(
"div:nth-child(2) > div:nth-child(4) > div > div > div.v-input__slot > div.v-select__slot > label"
)
def queued_job_type(self):
return self.page.locator("td:nth-child(2) > img >> nth=0").get_attribute('alt')

def click_content_server(self):
self.content_server_locator.click()
@property
def job_id_dialog_is_visible(self):
return self.page.is_visible("div.mdc-dialog__container")

@property
def job_id_dialog_title(self):
return self.page.locator("div.mdc-dialog__header")

@property
def content_server_dropdown(self):
return self.page.locator("div.v-menu__content")
def job_id_dialog_close_button(self):
return self.page.locator("div.mdc-dialog__actions > button:nth-child(2)")

def click_content_server_dropdown(self, value):
self.content_server_dropdown.locator(f"text={value}").click()
def click_job_id_dialog_close_button(self):
self.job_id_dialog_close_button.click()

@property
def status_message(self):
return self.page.locator(
"div:nth-child(3) > div > div > table > tbody > tr:nth-child(1) > td:nth-child(9)"
)
def version_sha(self):
return self.page.locator("div:nth-child(2) > div > div:nth-child(5)")

def remove_focus(self):
return self.page.keyboard.down("Tab")
def click_version_sha(self):
self.version_sha.click()
Loading

0 comments on commit 053774a

Please sign in to comment.