Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding QA tests for the new corgi UI #523

Merged
merged 7 commits into from
Jan 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 50 additions & 2 deletions .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 @@ -33,9 +81,9 @@ jobs:
command: |

cd ./backend/app
poetry install
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/"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not

@pytest.fixture
def corgi_base_url(base_url):
    """Return local corgi url"""
    return base_url

and use pytest --base-url "https://corgi-514.ce.openstax.org"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you are right

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