Initial commit

This commit is contained in:
E41222052_RurinNurhaliza 2026-06-07 22:57:40 +07:00
commit 701fd59bb4
58 changed files with 4480 additions and 0 deletions

17
.gitignore vendored Normal file
View File

@ -0,0 +1,17 @@
# Virtual Environment
venv/
# Python
__pycache__/
*.pyc
# Pytest
.pytest_cache/
# Selenium
chromedriver*
screenshots/
# OS
.DS_Store
Thumbs.db

27
README.md Normal file
View File

@ -0,0 +1,27 @@
# Selenium Automated Testing Project
Project ini digunakan untuk pengujian otomatis website
Adaptive Metacognitive Hypermedia Learning Environment (AMetative HLE)
menggunakan Selenium WebDriver dan framework pytest.
## Tools & Tech Stack
- Python 3.x
- Selenium WebDriver
- Pytest
- WebDriver Manager
## Cara Menjalankan Test
1. Clone repository
2. Install dependencies
pip install -r requirements.txt
3. Jalankan test
pytest -v
## Jenis Pengujian
- Functional Testing
- Integration Testing
- Automated Regression Test
## Author
Rurin Haliza

224
conftest.py Normal file
View File

@ -0,0 +1,224 @@
import os
import pytest
from datetime import datetime
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from pages.register_page import RegisterPage
from utils.data_generator import generate_valid_register_data
from pages.login_page import LoginPage
from pages.dashboard_page import DashboardPage
# ===============================
# FIXTURE DRIVER
# ===============================
@pytest.fixture(scope="function")
def driver():
options = Options()
options.add_argument("--start-maximized")
service = Service()
driver = webdriver.Chrome(service=service, options=options)
yield driver
driver.quit()
# ===============================
# FIXTURE REGISTER PAGE
# ===============================
@pytest.fixture
def register_page(driver):
page = RegisterPage(driver)
page.open()
return page
# ===============================
# DATA REGISTER VALID (FINAL)
# ===============================
@pytest.fixture
def valid_register_data():
"""
- Data selalu VALID
- Data SELALU BARU setiap test
- Field boleh dioverride di test
"""
data = generate_valid_register_data()
# pastikan field WAJIB selalu ada
data.setdefault("nama_lengkap", data["nama"])
data.setdefault("konfirmasi_password", data["password"])
return data
# ===============================
# SCREENSHOT SAAT FAIL
# ===============================
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
"""
Ambil screenshot otomatis jika test FAIL
"""
outcome = yield
rep = outcome.get_result()
if rep.when == "call" and rep.failed:
driver = item.funcargs.get("driver")
if driver:
screenshots_dir = "screenshots"
os.makedirs(screenshots_dir, exist_ok=True)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
file_path = os.path.join(
screenshots_dir,
f"{item.name}_{timestamp}.png"
)
driver.save_screenshot(file_path)
print(f"\n📸 Screenshot saved: {file_path}")
# ===============================
# FIXTURE LOGIN PAGE
# ===============================
@pytest.fixture
def login_page(driver):
page = LoginPage(driver)
page.open()
return page
# =========================
# LOGIN FIXTURES FOR TEST
# =========================
@pytest.fixture
def login_as_user_belum_kuesioner(driver):
"""
User valid, BELUM pernah mengisi kuesioner
Digunakan untuk:
- popup dashboard
- status KM / RM kosong
"""
login_page = LoginPage(driver)
login_page.open()
login_page.fill_email("e41222052@student.polije.ac.id")
login_page.fill_password("e41222052@student.polije.ac.id")
login_page.submit()
dashboard = DashboardPage(driver)
dashboard.open()
return driver
@pytest.fixture
def login_as_user_sudah_kuesioner(driver):
"""
User valid, SUDAH pernah mengisi kuesioner
Digunakan untuk:
- status KM / RM tersedia
- tidak ada popup
"""
login_page = LoginPage(driver)
login_page.open()
# Akun yang SUDAH isi kuesioner
login_page.fill_email("tester@polije.ac.id")
login_page.fill_password("tester@polije.ac.id")
login_page.submit()
dashboard = DashboardPage(driver)
dashboard.open()
return driver
@pytest.fixture
def login_as_user_belum_kuesioner2(driver):
"""
User valid, SUDAH pernah mengisi kuesioner
Digunakan untuk:
- status KM / RM tersedia
- tidak ada popup
"""
login_page = LoginPage(driver)
login_page.open()
# Akun yang SUDAH isi kuesioner
login_page.fill_email("akuntesting2@polije.ac.id")
login_page.fill_password("akuntesting2@polije.ac.id")
login_page.submit()
dashboard = DashboardPage(driver)
dashboard.open()
return driver
#Account For Regression
@pytest.fixture
def login_as_user_belum_kuesionerNew(driver):
"""
User valid, SUDAH pernah mengisi kuesioner
Digunakan untuk:
- status KM / RM tersedia
- tidak ada popup
"""
login_page = LoginPage(driver)
login_page.open()
# Akun yang SUDAH isi kuesioner
login_page.fill_email("tester2@polije.ac.id")
login_page.fill_password("tester2@polije.ac.id")
login_page.submit()
dashboard = DashboardPage(driver)
dashboard.open()
return driver
@pytest.fixture
def login_as_user_belum_kuesionerNew2(driver):
"""
User valid, SUDAH pernah mengisi kuesioner
Digunakan untuk:
- status KM / RM tersedia
- tidak ada popup
"""
login_page = LoginPage(driver)
login_page.open()
# Akun yang SUDAH isi kuesioner
login_page.fill_email("iniemail@polije.ac.id")
login_page.fill_password("inipassword")
login_page.submit()
dashboard = DashboardPage(driver)
dashboard.open()
return driver
@pytest.fixture
def login_as_user_sudah_kuesioner3(driver):
"""
User valid, SUDAH pernah mengisi kuesioner
Digunakan untuk:
- status KM / RM tersedia
- tidak ada popup
"""
login_page = LoginPage(driver)
login_page.open()
# Akun yang SUDAH isi kuesioner
login_page.fill_email("sudah@polije.ac.id")
login_page.fill_password("sudah@polije.ac.id")
login_page.submit()
dashboard = DashboardPage(driver)
dashboard.open()
return driver

206
pages/dashboard_page.py Normal file
View File

@ -0,0 +1,206 @@
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import re
class DashboardPage:
URL = "https://hypermedialearning.project2025.id/public/dashboard"
# ===== POPUP (MODAL) =====
POPUP_MODAL = (By.ID, "kuesionerModal")
BTN_ISI_KUESIONER_POPUP = (
By.XPATH, "//div[@id='kuesionerModal']//a[contains(text(),'Isi Kuesioner')]"
)
BTN_TUTUP_POPUP = (
By.XPATH, "//div[@id='kuesionerModal']//button[contains(text(),'Tutup')]"
)
BTN_CLOSE_X = (
By.XPATH, "//div[@id='kuesionerModal']//button[@aria-label='Close']"
)
MODAL_KUESIONER = (By.ID, "kuesionerModal")
BTN_TUTUP_MODAL = (
By.XPATH,
"//button[contains(text(),'Nanti') or contains(text(),'Tutup')]"
)
KM_VALUE = (
By.XPATH,
"//span[normalize-space()='Knowledge of Metakognitif (KM)']"
"/ancestor::div[contains(@class,'card')]"
"//span[normalize-space()='Nilai']/following-sibling::span"
)
RM_VALUE = (
By.XPATH,
"//span[normalize-space()='Regulation of Metakognitif (RM)']"
"/ancestor::div[contains(@class,'card')]"
"//span[normalize-space()='Nilai']/following-sibling::span"
)
LEARNING_STYLE_VALUE = (
By.XPATH,
"//span[normalize-space()='Learning Style']"
"/ancestor::div[contains(@class,'card')]"
"//span[normalize-space()='Gaya Belajar']/following-sibling::span"
)
PENGISIAN_KUESIONER_SECTION = (
By.XPATH,
"//h6[normalize-space()='Pengisian Kuesioner']/ancestor::div[contains(@class,'card')]"
)
KUESIONER_VARK_MAI_TEXT = (
By.XPATH,
"//td[normalize-space()='Kuesioner VARK dan MAI']"
)
ISI_KUESIONER_BUTTON = (
By.XPATH,
"//a[@href='https://hypermedialearning.sanggadewa.my.id/kuesioner-panduan']"
)
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 15)
def open(self):
self.driver.get(self.URL)
# POM for test dashboard PopUp
def is_popup_visible(self):
try:
self.wait.until(
EC.visibility_of_element_located(self.POPUP_MODAL)
)
return True
except:
return False
def click_isi_kuesioner_pop_up(self):
self.wait.until(
EC.element_to_be_clickable(self.BTN_ISI_KUESIONER_POPUP)
).click()
def close_popup_with_x(self):
btn = self.wait.until(
EC.visibility_of_element_located(self.BTN_CLOSE_X)
)
btn.click()
self.wait_popup_disappear()
def is_redirected_to_kuesioner(self):
self.wait.until(lambda d: "kuesioner" in d.current_url)
return True
def wait_popup_disappear(self):
self.wait.until(
EC.invisibility_of_element_located(self.POPUP_MODAL)
)
#POM for Test Dashboard KMRM
def close_kuesioner_popup_if_present(self):
btn = self.wait.until(
EC.visibility_of_element_located(self.BTN_CLOSE_X)
)
btn.click()
self.wait_popup_disappear()
def get_km_status_text(self):
return self.wait.until(
EC.visibility_of_element_located(self.KM_VALUE)
).text.strip()
def get_rm_status_text(self):
return self.wait.until(
EC.visibility_of_element_located(self.RM_VALUE)
).text.strip()
def is_km_not_filled(self):
return self.get_km_status_text() == "()"
def is_rm_not_filled(self):
return self.get_rm_status_text() == "()"
def is_km_filled(self):
el = self.wait.until(
EC.visibility_of_element_located(self.KM_VALUE)
)
return el.text.strip()
def is_rm_filled(self):
el = self.wait.until(
EC.visibility_of_element_located(self.RM_VALUE)
)
return el.text.strip()
#POM for Learning Style
def get_learning_style_text(self) -> str:
el = self.wait.until(
EC.visibility_of_element_located(self.LEARNING_STYLE_VALUE)
)
return el.text.strip()
def is_learning_style_not_filled(self) -> bool:
return self.get_learning_style_text() == "(Tidak diketahui)"
def is_learning_style_filled(self) -> bool:
text = self.get_learning_style_text()
return text != "" and text != "(Tidak diketahui)"
#POM for Pengisian Kuesioner
def is_pengisian_kuesioner_section_visible(self) -> bool:
try:
self.wait.until(
EC.visibility_of_element_located(
self.PENGISIAN_KUESIONER_SECTION
)
)
return True
except:
return False
def is_kuesioner_vark_mai_visible(self) -> bool:
try:
self.wait.until(
EC.visibility_of_element_located(
self.KUESIONER_VARK_MAI_TEXT
)
)
return True
except:
return False
def click_isi_kuesioner(self):
self.wait.until(
EC.element_to_be_clickable(
self.ISI_KUESIONER_BUTTON
)
).click()

View File

@ -0,0 +1,186 @@
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class HistoryKuesionerPage:
URL = "https://hypermedialearning.sanggadewa.my.id/history_quis"
# ===== SIDEBAR =====
SIDEBAR_HISTORY = (
By.XPATH,
"//li[contains(@class,'menu-item') and .//div[normalize-space()='History Kuisioner']]"
)
# ===== SKOR GAYA BELAJAR =====
SCORE_CONTAINER = (By.CSS_SELECTOR, "div.card")
SCORE_LABELS = (By.CSS_SELECTOR, "div.score-label")
SCORE_VALUES = (By.CSS_SELECTOR, "div.score-value")
LEARNING_STYLE_CARD = (
By.XPATH,
"//div[contains(@class,'card') and .//div[text()='Visual']]"
)
LEARNING_STYLE_LABELS = (
By.XPATH,
"//div[contains(@class,'card') and .//div[text()='Visual']]//div[@class='score-label']"
)
LEARNING_STYLE_VALUES = (
By.XPATH,
"//div[contains(@class,'card') and .//div[text()='Visual']]//div[@class='score-value']"
)
LEARNING_STYLE_ITEMS = (
By.CSS_SELECTOR,
"div.card.shadow.border-0.p-4 > div.row.text-center > div.col-6.col-md-3"
)
# ===== PROGRESS =====
PROGRESS_BAR = (By.CSS_SELECTOR, "div.progress-bar")
PROGRESS_TEXT = (
By.XPATH,
"//small[contains(text(),'pertanyaan dijawab')]"
)
# ===== GAYA BELAJAR DOMINAN =====
DOMINANT_STYLE_TEXT = (
By.XPATH,
"//h2[contains(@class,'fw-bold')]"
)
BTN_MULAI_BELAJAR = (
By.XPATH,
"//a[contains(@href,'/materi') and contains(.,'Mulai')]"
)
# ===== KM & RM =====
KM_VALUE = (
By.XPATH,
"//div[normalize-space()='KM']/following-sibling::div[contains(@class,'score-value')]"
)
RM_VALUE = (
By.XPATH,
"//div[normalize-space()='RM']/following-sibling::div[contains(@class,'score-value')]"
)
# ===== RIWAYAT =====
TABLE_ROWS = (By.CSS_SELECTOR, "table.table tbody tr")
# ===== AKSI =====
BTN_MULAI_BELAJAR = (
By.XPATH,
"//a[contains(@href,'/materi')]"
)
BTN_UBAH = (
By.XPATH,
"//a[contains(@href,'kuesioner')]"
)
BTN_UNDUH = (
By.XPATH,
"//a[contains(@href,'user-result')]"
)
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
def open(self):
self.driver.get(self.URL)
def is_page_loaded(self):
self.wait.until(EC.presence_of_element_located(self.SIDEBAR_HISTORY))
return True
def get_scores(self):
"""
Return skor gaya belajar saja.
PASSED = benar-benar 4 gaya belajar.
"""
items = self.wait.until(
lambda d: d.find_elements(*self.LEARNING_STYLE_ITEMS)
)
assert len(items) == 4, f"Expected 4 learning styles, found {len(items)}"
scores = {}
for item in items:
label = item.find_element(By.CSS_SELECTOR, ".score-label").text.strip()
value = item.find_element(By.CSS_SELECTOR, ".score-value").text.strip()
scores[label] = value
return scores
def get_score_labels(self):
# tunggu card halaman muncul dulu
self.wait.until(
EC.presence_of_element_located(self.SCORE_CONTAINER)
)
# baru ambil labels
self.wait.until(
lambda d: len(d.find_elements(*self.SCORE_LABELS)) >= 4
)
return [
e.text.strip()
for e in self.driver.find_elements(*self.SCORE_LABELS)
if e.text.strip() != ""
]
def get_score_values(self):
self.wait.until(
lambda d: all(
e.text.strip().isdigit()
for e in d.find_elements(*self.LEARNING_STYLE_VALUES)
)
)
return [e.text.strip() for e in self.driver.find_elements(*self.LEARNING_STYLE_VALUES)]
def get_progress_value(self):
bar = self.wait.until(EC.presence_of_element_located(self.PROGRESS_BAR))
return bar.get_attribute("aria-valuenow")
def get_progress_text(self):
return self.driver.find_element(*self.PROGRESS_TEXT).text
def get_dominant_style(self):
return self.wait.until(
EC.presence_of_element_located(self.DOMINANT_STYLE_TEXT)
).text.strip()
def click_mulai(self):
button = self.wait.until(
EC.presence_of_element_located(self.BTN_MULAI_BELAJAR)
)
# Scroll ke elemen (cukup)
self.driver.execute_script(
"arguments[0].scrollIntoView({block: 'center'});", button
)
# Klik pakai JS (paling stabil untuk <a>)
self.driver.execute_script("arguments[0].click();", button)
def get_km_value(self):
return self.driver.find_element(*self.KM_VALUE).text.lower()
def get_rm_value(self):
return self.driver.find_element(*self.RM_VALUE).text.lower()
def get_history_rows(self):
return self.driver.find_elements(*self.TABLE_ROWS)
def click_ubah(self):
self.wait.until(EC.element_to_be_clickable(self.BTN_UBAH)).click()
def click_unduh(self):
button = self.wait.until(EC.element_to_be_clickable(self.BTN_UNDUH))
self.driver.execute_script("arguments[0].scrollIntoView(true);", button)
self.driver.execute_script("arguments[0].click();", button)

View File

@ -0,0 +1,85 @@
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class KuesionerLSPage:
DASHBOARD_MENU = (
By.XPATH,
"//a[contains(@href,'dashboard') or .//div[normalize-space()='Dashboard']]"
)
SUBMIT_BUTTON = (
By.XPATH, "//button[normalize-space()='Simpan Jawaban']"
)
KUESIONER_MENU = (
By.XPATH,
"//a[contains(@href,'kuesioner-panduan') and .//div[normalize-space()='Kuesioner']]"
)
MULAI_KUESIONER_BUTTON = (
By.XPATH,
"//a[contains(@href,'/kuesioner-ls') and normalize-space()='Mulai Kuesioner']"
)
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
def open_from_sidebar(self):
self.wait.until(
EC.element_to_be_clickable(self.KUESIONER_MENU)
).click()
def click_mulai_kuesioner(self):
self.wait.until(
EC.element_to_be_clickable(self.MULAI_KUESIONER_BUTTON)
).click()
def answer_question(self, question_number, option_index=0):
"""
option_index: 0-3 (LS punya 4 opsi)
"""
options = self.wait.until(
EC.presence_of_all_elements_located(
(By.NAME, f"soal{question_number}")
)
)
option = options[option_index]
# WAJIB scroll dulu
self.driver.execute_script(
"arguments[0].scrollIntoView({block: 'center'});", option
)
self.wait.until(EC.element_to_be_clickable(option)).click()
def answer_all_questions(self, total_questions=16):
for i in range(1, total_questions + 1):
self.answer_question(i)
def submit(self):
button = self.wait.until(
EC.presence_of_element_located(self.SUBMIT_BUTTON)
)
self.driver.execute_script(
"arguments[0].scrollIntoView({block: 'center'});", button
)
self.wait.until(EC.element_to_be_clickable(self.SUBMIT_BUTTON)).click()
def force_navigate_to_dashboard(self):
menu = self.wait.until(
EC.presence_of_element_located(self.DASHBOARD_MENU)
)
self.driver.execute_script(
"arguments[0].scrollIntoView({block: 'center'});", menu
)
self.wait.until(EC.element_to_be_clickable(self.DASHBOARD_MENU)).click()

101
pages/kuesioner_mai_page.py Normal file
View File

@ -0,0 +1,101 @@
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class KuesionerMAIPage:
TITLE = (
By.XPATH,
"//h3[contains(text(),'Kuesioner Metakognitif')]"
)
SUBMIT_BUTTON = (
By.XPATH,
"//button[normalize-space()='Simpan Jawaban']"
)
KUESIONER_MENU = (
By.XPATH,
"//a[contains(@href,'kuesioner-panduan') and .//div[normalize-space()='Kuesioner']]"
)
MULAI_KUESIONER_BUTTON = (
By.XPATH,
"//a[contains(@href,'/kuesioner-ls') and normalize-space()='Mulai Kuesioner']"
)
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 15)
def wait_until_loaded(self):
self.wait.until(
EC.presence_of_element_located(self.TITLE)
)
def answer_question_mai(self, question_number, option_index=0):
"""
option_index: 04 (nilai 04)
"""
radios = self.driver.find_elements(
By.NAME, f"soal{question_number}"
)
target = radios[option_index]
# WAJIB scroll agar tidak intercepted
self.driver.execute_script(
"arguments[0].scrollIntoView({block:'center'});",
target
)
self.wait.until(EC.element_to_be_clickable(target)).click()
def answer_question_ls(self, question_number, option_index=0):
"""
option_index: 0-3 (LS punya 4 opsi)
"""
options = self.wait.until(
EC.presence_of_all_elements_located(
(By.NAME, f"soal{question_number}")
)
)
option = options[option_index]
# WAJIB scroll dulu
self.driver.execute_script(
"arguments[0].scrollIntoView({block: 'center'});", option
)
self.wait.until(EC.element_to_be_clickable(option)).click()
def answer_all_questions_ls(self, total_questions=16):
for i in range(1, total_questions + 1):
self.answer_question_ls(i)
def answer_all_questions_mai(self, total_questions=52):
for i in range(1, total_questions + 1):
self.answer_question_mai(i)
def submit(self):
btn = self.wait.until(
EC.element_to_be_clickable(self.SUBMIT_BUTTON)
)
self.driver.execute_script(
"arguments[0].scrollIntoView({block:'center'});",
btn
)
btn.click()
def open_from_sidebar(self):
self.wait.until(
EC.element_to_be_clickable(self.KUESIONER_MENU)
).click()
def click_mulai_kuesioner(self):
self.wait.until(
EC.element_to_be_clickable(self.MULAI_KUESIONER_BUTTON)
).click()

View File

@ -0,0 +1,52 @@
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class KuesionerPanduanPage:
URL = "https://hypermedialearning.sanggadewa.my.id/kuesioner-panduan"
# ===== LOCATORS =====
KUESIONER_MENU = (
By.XPATH,
"//a[contains(@href,'kuesioner-panduan') and .//div[normalize-space()='Kuesioner']]"
)
MULAI_KUESIONER_BUTTON = (
By.XPATH,
"//a[contains(@href,'/kuesioner-ls') and normalize-space()='Mulai Kuesioner']"
)
BANTUAN_BUTTON = (
By.XPATH,
"//a[contains(@href,'wa.me') and contains(normalize-space(),'Bantuan')]"
)
# ===== INIT =====
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 15)
# ===== ACTIONS =====
def open_from_sidebar(self):
self.wait.until(
EC.element_to_be_clickable(self.KUESIONER_MENU)
).click()
def click_mulai_kuesioner(self):
self.wait.until(
EC.element_to_be_clickable(self.MULAI_KUESIONER_BUTTON)
).click()
def click_bantuan(self):
self.wait.until(
EC.element_to_be_clickable(self.BANTUAN_BUTTON)
).click()
# ===== ASSERTIONS =====
def is_on_panduan_page(self) -> bool:
return "kuesioner-panduan" in self.driver.current_url

151
pages/live_coding_page.py Normal file
View File

@ -0,0 +1,151 @@
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import NoAlertPresentException
class LiveCodingPage:
# ===== LOCATORS =====
PAGE_TITLE = (By.XPATH, "//h4[contains(text(),'Live Coding')]")
EXPECTED_OUTPUT = (By.XPATH, "//strong[contains(text(),'Hasil Output')]")
CODE_EDITOR = (By.ID, "editor")
SUBMIT_BUTTON = (By.ID, "submit-code")
ERROR_404_TEXT = "404"
#alert / result
RESULT_MESSAGE = (By.ID, "result")
MODAL = (By.CLASS_NAME, "modal-dialog")
CLOSE_BUTTON = (By.XPATH, "//button[normalize-space()='Tutup']")
PREVIOUS_BUTTON = (By.XPATH, "//a[contains(text(),'Sebelumnya')]")
NEXT_BUTTON = (By.XPATH, "//a[contains(text(),'Selanjutnya')]")
OTHER_MENU = (By.XPATH, "//a[contains(@href,'/materi/visual/')]")
CONFIRM_MODAL = (By.CLASS_NAME, "modal")
CONFIRM_TEXT = (By.CLASS_NAME, "modal-body")
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
# ===== PAGE ACTIONS =====
def is_page_loaded(self):
self.wait.until(EC.visibility_of_element_located(self.PAGE_TITLE))
return True
def get_expected_output_text(self):
return self.driver.find_element(*self.EXPECTED_OUTPUT).text
def editor_is_visible(self):
return self.driver.find_element(*self.CODE_EDITOR).is_displayed()
def append_code(self, code):
"""
Menambahkan kode ke editor (tanpa menghapus kode bawaan)
"""
editor = self.driver.find_element(*self.CODE_EDITOR)
editor.click()
actions = ActionChains(self.driver)
actions.key_down(Keys.CONTROL).send_keys(Keys.END).key_up(Keys.CONTROL)
actions.send_keys(Keys.ENTER)
actions.send_keys(code)
actions.perform()
def append_code2(self, code):
current_code = self.driver.execute_script("""
return monaco.editor.getModels()[0].getValue();
""")
updated_code = current_code
updated_code = updated_code.replace("...............", " Mahasiswa")
updated_code = updated_code.replace(".........", ".println")
self.driver.execute_script("""
monaco.editor.getModels()[0].setValue(arguments[0]);
""", updated_code)
def submit_code(self):
btn = WebDriverWait(self.driver, 15).until(
EC.element_to_be_clickable(self.SUBMIT_BUTTON)
)
self.driver.execute_script(
"arguments[0].scrollIntoView({block:'center'});", btn
)
self.driver.execute_script("arguments[0].click();", btn)
def get_result_message(self):
wait = WebDriverWait(self.driver, 20)
def result_has_text(driver):
el = driver.find_element(By.ID, "result")
return el if el.text.strip() != "" else False
result_el = wait.until(result_has_text)
return result_el.text
def close_result_modal(self):
WebDriverWait(self.driver, 10).until(
EC.element_to_be_clickable(self.CLOSE_BUTTON)
).click()
def get_editor_value(self):
"""
Ambil isi editor Monaco via JavaScript
"""
return self.driver.execute_script(
"return monaco.editor.getModels()[0].getValue();"
)
def wait_popup_visible(self):
self.wait.until(EC.visibility_of_element_located(self.RESULT_MESSAGE))
def is_popup_visible(self):
try:
return self.driver.find_element(*self.RESULT_MESSAGE).is_displayed()
except:
return False
def click_previous(self):
element = self.driver.find_element(*self.PREVIOUS_BUTTON)
self.driver.execute_script("arguments[0].scrollIntoView(true);", element)
element.click()
def click_next(self):
element = self.driver.find_element(*self.NEXT_BUTTON)
self.driver.execute_script("arguments[0].scrollIntoView(true);", element)
element.click()
def wait_popup_invisible(self):
WebDriverWait(self.driver, 10).until(
EC.invisibility_of_element_located((By.ID, "resultModal"))
)
# ===== INFO =====
def get_current_url(self):
return self.driver.current_url
def is_404_page(self):
return self.ERROR_404_TEXT in self.driver.page_source
def click_other_menu(self):
self.driver.find_element(*self.OTHER_MENU).click()
# ===== ALERT HANDLER =====
def is_confirm_alert_present(self):
try:
alert = self.driver.switch_to.alert
return alert.text
except NoAlertPresentException:
return None
# ===== MODAL HANDLER =====
def is_confirm_modal_visible(self):
try:
return self.driver.find_element(*self.CONFIRM_MODAL).is_displayed()
except:
return False

169
pages/login_page.py Normal file
View File

@ -0,0 +1,169 @@
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class LoginPage:
#URL = "https://hypermedialearning.sanggadewa.my.id/login"
URL = "https://hypermedialearning.project2025.id/public/login"
DASHBOARD_URL_PART = "/dashboard"
# ===============================
# LOCATORS
# ===============================
EMAIL = (By.ID, "email")
PASSWORD = (By.ID, "password")
REMEMBER_ME = (By.NAME, "remember")
BUTTON_LOGIN = (By.XPATH, "//button[@type='submit']")
ERROR_MESSAGE = (By.CLASS_NAME, "invalid-feedback")
GLOBAL_ERROR = (
By.XPATH,
"//*[contains(text(),'These credentials do not match our records')]"
)
SSO_GOOGLE_BUTTON = (By.XPATH, "//a[contains(@href,'/auth/google')]")
# ===============================
# INIT
# ===============================
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
# ===============================
# PAGE ACTIONS
# ===============================
def open(self):
self.driver.get(self.URL)
def fill_email(self, value: str):
field = self.wait.until(
EC.visibility_of_element_located(self.EMAIL)
)
field.clear()
field.send_keys(value)
def fill_password(self, value: str):
field = self.wait.until(
EC.visibility_of_element_located(self.PASSWORD)
)
field.clear()
field.send_keys(value)
def click_login(self):
self.wait.until(
EC.element_to_be_clickable(self.BUTTON_LOGIN)
).click()
def click_remember_me(self):
checkbox = self.wait.until(
EC.element_to_be_clickable(self.REMEMBER_ME)
)
if not checkbox.is_selected():
checkbox.click()
def click_google_sso(self):
self.wait.until(
EC.element_to_be_clickable(self.GOOGLE_SSO_BUTTON)
).click()
# ===============================
# BUSINESS / HELPER METHODS
# ===============================
def login(self, email: str, password: str):
self.fill_email(email)
self.fill_password(password)
self.click_login()
def is_login_success(self) -> bool:
try:
self.wait.until(
lambda d: self.DASHBOARD_URL_PART in d.current_url
)
return True
except:
return False
def is_login_failed(self) -> bool:
return self.has_error_message()
def has_error_message(self) -> bool:
return any(
el.text.strip()
for el in self.driver.find_elements(*self.ERROR_MESSAGE)
)
def has_global_error(self) -> bool:
try:
return self.driver.find_element(
*self.GLOBAL_ERROR
).is_displayed()
except:
return False
def is_field_required(self, field_name: str) -> bool:
fields = {
"email": self.EMAIL,
"password": self.PASSWORD
}
locator = fields.get(field_name)
if not locator:
raise ValueError(f"Field '{field_name}' tidak dikenali")
element = self.driver.find_element(*locator)
return element.get_attribute("required") is not None
def get_email_validation_message(self):
email = self.driver.find_element(By.ID, "email")
return self.driver.execute_script(
"return arguments[0].validationMessage;",
email
)
def submit(self):
self.click_login()
def has_html5_validation(self, field_name: str) -> bool:
field = self.get_field(field_name)
return field.get_attribute("validationMessage") != ""
def get_field(self, field_name: str):
fields = {
"email": self.EMAIL,
"password": self.PASSWORD,
}
locator = fields.get(field_name)
if not locator:
raise ValueError(f"Field '{field_name}' tidak dikenali di LoginPage")
return self.driver.find_element(*locator)
def click_remember_me(self):
checkbox = self.wait.until(
EC.element_to_be_clickable(self.REMEMBER_ME)
)
if not checkbox.is_selected():
checkbox.click()
def is_remember_me_checked(self) -> bool:
checkbox = self.driver.find_element(*self.REMEMBER_ME)
return checkbox.is_selected()
def click_sso_google(self):
self.wait.until(
EC.element_to_be_clickable(self.SSO_GOOGLE_BUTTON)
).click()
def is_redirected_to_google(self) -> bool:
self.wait.until(
lambda d: "accounts.google.com" in d.current_url
)
return "accounts.google.com" in self.driver.current_url

82
pages/logout_page.py Normal file
View File

@ -0,0 +1,82 @@
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class LogoutPage:
URL = "https://hypermedialearning.sanggadewa.my.id/dashboard"
# --- USER ICON (pojok kanan atas) ---
USER_ICON = (
By.XPATH,
"//li[contains(@class,'dropdown')]//a[contains(@class,'dropdown-toggle')]"
)
# --- MENU DROPDOWN ---
LOGOUT_MENU = (By.XPATH,"//a[@data-bs-target='#confirmlogout']")
# --- MODAL ---
LOGOUT_MODAL = (By.ID, "confirmlogout")
MODAL_TITLE = (By.ID, "confirmlogoutLabel")
# --- BUTTON DI MODAL ---
BUTTON_TIDAK = (
By.XPATH,
"//div[@id='confirmlogout']//button[contains(text(),'Tidak')]"
)
BUTTON_YA = (
By.XPATH,
"//div[@id='confirmlogout']//button[contains(text(),'Ya')]"
)
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 15)
def open(self):
self.driver.get(self.URL)
# =============================
# ACTIONS
# =============================
def open_user_dropdown(self):
dropdown = self.wait.until(
EC.element_to_be_clickable(self.USER_ICON)
)
dropdown.click()
# Tunggu logout menu muncul
self.wait.until(
EC.visibility_of_element_located(self.LOGOUT_MENU)
)
def click_logout_menu(self):
self.wait.until(
EC.element_to_be_clickable(self.LOGOUT_MENU)
).click()
def is_logout_modal_visible(self):
return self.wait.until(
EC.visibility_of_element_located(self.MODAL_TITLE)
)
def click_tidak(self):
self.wait.until(
EC.element_to_be_clickable(self.BUTTON_TIDAK)
).click()
def click_ya_logout(self):
self.wait.until(
EC.element_to_be_clickable(self.BUTTON_YA)
).click()
def wait_until_redirect_to_home(self):
self.wait.until(
EC.url_to_be("https://hypermedialearning.sanggadewa.my.id/")
)

50
pages/materi_page.py Normal file
View File

@ -0,0 +1,50 @@
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class MateriPage:
# ===== SIDEBAR =====
SIDEBAR = (By.ID, "learningStyleSidebar")
BTN_VISUAL = (
By.XPATH,
"//div[@id='learningStyleSidebar']//h6[normalize-space()='Visual']/following-sibling::a"
)
BTN_AUDITORY = (
By.XPATH,
"//div[@id='learningStyleSidebar']//h6[normalize-space()='Auditory']/following-sibling::a"
)
# ===== JUDUL HALAMAN =====
TITLE_VISUAL = (By.XPATH, "//h4[normalize-space()='Materi Visual']")
TITLE_AUDITORY = (By.XPATH, "//h4[normalize-space()='Materi Auditory']")
# ===== MEDIA =====
VISUAL_IMAGE = (By.XPATH, "//img[@alt='Gambar Materi']")
AUDIO_PLAYER = (By.XPATH, "//audio")
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
# ===== SIDEBAR =====
def sidebar_visible(self):
self.wait.until(EC.visibility_of_element_located(self.SIDEBAR))
def go_to_visual(self):
self.wait.until(EC.element_to_be_clickable(self.BTN_VISUAL)).click()
def go_to_auditory(self):
self.wait.until(EC.element_to_be_clickable(self.BTN_AUDITORY)).click()
# ===== ASSERTION =====
def visual_page_loaded(self):
self.wait.until(EC.visibility_of_element_located(self.TITLE_VISUAL))
self.wait.until(EC.visibility_of_element_located(self.VISUAL_IMAGE))
def auditory_page_loaded(self):
self.wait.until(EC.visibility_of_element_located(self.TITLE_AUDITORY))
self.wait.until(EC.visibility_of_element_located(self.AUDIO_PLAYER))

View File

@ -0,0 +1,105 @@
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class MateriPembelajaranPage:
# ===== SIDEBAR =====
MENU_MATERI_PEMBELAJARAN = (
By.XPATH,
"//div[normalize-space()='Materi Pembelajaran']/ancestor::a"
)
SUBMENU_ENKAPSULASI = (
By.XPATH,
"//a[@href='/materi']//div[normalize-space()='Enkapsulasi']"
)
# ===== LEARNING STYLE UTAMA =====
LEARNING_STYLE_TITLE = (
By.XPATH,
"//h5[normalize-space()='Learning Style Kamu 🎉']"
)
LEARNING_STYLE_VALUE = (
By.XPATH,
"//h5[normalize-space()='Learning Style Kamu 🎉']/following-sibling::p"
)
BUTTON_MULAI_BELAJAR_UTAMA = (
By.XPATH,
"//h5[normalize-space()='Learning Style Kamu 🎉']/following::a[normalize-space()='Mulai Belajar']"
)
# ===== PESAN ALTERNATIF =====
PESAN_ALTERNATIF = (
By.XPATH,
"//h5[contains(text(),'Materi Sesuai Learning Style Kamu Sulit Dipahami')]"
)
# ===== CARD ALTERNATIF =====
CARD_VISUAL = (
By.XPATH,
"//h5[normalize-space()='Visual']/following::a[normalize-space()='Mulai Belajar'][1]"
)
CARD_AUDITORY = (
By.XPATH,
"//h5[normalize-space()='Auditory']/following::a[normalize-space()='Mulai Belajar'][1]"
)
CARD_READ_WRITE = (
By.XPATH,
"//h5[normalize-space()='Read/ Write']/following::a[normalize-space()='Mulai Belajar'][1]"
)
# ===== JUDUL HALAMAN MATERI =====
JUDUL_HALAMAN_MATERI = (
By.XPATH,
"//h4[@class='fw-bold mb-4']"
)
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
# ===== ACTIONS =====
def open_from_sidebar(self):
self.wait.until(
EC.element_to_be_clickable(self.MENU_MATERI_PEMBELAJARAN)
).click()
self.wait.until(
EC.element_to_be_clickable(self.SUBMENU_ENKAPSULASI)
).click()
def click_mulai_belajar_utama(self):
self.wait.until(
EC.element_to_be_clickable(self.BUTTON_MULAI_BELAJAR_UTAMA)
).click()
def click_visual(self):
self.wait.until(
EC.element_to_be_clickable(self.CARD_VISUAL)
).click()
def click_auditory(self):
self.wait.until(
EC.element_to_be_clickable(self.CARD_AUDITORY)
).click()
def click_read_write(self):
self.wait.until(
EC.element_to_be_clickable(self.CARD_READ_WRITE)
).click()
def get_learning_style_user(self):
return self.wait.until(
EC.visibility_of_element_located(self.LEARNING_STYLE_VALUE)
).text
def get_judul_materi(self):
return self.wait.until(
EC.visibility_of_element_located(self.JUDUL_HALAMAN_MATERI)
).text

View File

@ -0,0 +1,76 @@
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class MateriReadWritePage:
# ===== Locator =====
PAGE_TITLE = (By.XPATH, "//h4[normalize-space()='Materi Read / Write']")
TEXT_MATERI = (By.XPATH, "//strong[text()='Teks:']/following-sibling::pre")
RANGKUMAN_TEXTAREA = (By.ID, "rangkuman")
SUBMIT_BUTTON = (By.XPATH, "//button[normalize-space()='Kirim Rangkuman']")
SUCCESS_ALERT = (By.XPATH, "//div[contains(@class,'alert-success')]")
TUGAS_RANGKUMAN_LABEL = (By.XPATH,"//label[@for='rangkuman']//strong[normalize-space()='Tugas Rangkuman:']")
# Sidebar navigasi materi lain
SIDEBAR_VISUAL = (
By.XPATH,
"//div[@id='learningStyleSidebar']//h6[normalize-space()='Visual']/following-sibling::a"
)
SIDEBAR_AUDITORY = (
By.XPATH,
"//div[@id='learningStyleSidebar']//h6[normalize-space()='Auditory']/following-sibling::a"
)
SIDEBAR_KINESTHETIC = (
By.XPATH,
"//div[@id='learningStyleSidebar']//h6[normalize-space()='Kinesthetic']/following-sibling::a"
)
ERROR_ALERT = (By.XPATH, "//div[contains(@class,'alert-danger')]")
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
# ===== Actions =====
def page_loaded(self):
self.wait.until(EC.visibility_of_element_located(self.PAGE_TITLE))
def get_text_materi(self):
return self.wait.until(
EC.visibility_of_element_located(self.TEXT_MATERI)
).text
def input_rangkuman(self, text):
textarea = self.wait.until(
EC.visibility_of_element_located(self.RANGKUMAN_TEXTAREA)
)
textarea.clear()
textarea.send_keys(text)
def submit_rangkuman(self):
self.wait.until(
EC.element_to_be_clickable(self.SUBMIT_BUTTON)
).click()
def success_message_displayed(self):
return self.wait.until(
EC.visibility_of_element_located(self.SUCCESS_ALERT)
)
# ===== Sidebar navigation =====
def go_to_visual(self):
self.wait.until(EC.element_to_be_clickable(self.SIDEBAR_VISUAL)).click()
def go_to_auditory(self):
self.wait.until(EC.element_to_be_clickable(self.SIDEBAR_AUDITORY)).click()
def go_to_kinesthetic(self):
self.wait.until(EC.element_to_be_clickable(self.SIDEBAR_KINESTHETIC)).click()
def error_message_displayed(self):
return self.wait.until(
EC.visibility_of_element_located(self.ERROR_ALERT)
)

193
pages/profile_page.py Normal file
View File

@ -0,0 +1,193 @@
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import Select
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class ProfilePage:
# ===============================
# LOCATORS
# ===============================
# --- Profile Display ---
EMAIL_FIELD = (By.ID, "email")
NAMA_LENGKAP_FIELD = (By.ID, "nama_lengkap")
NIM_FIELD = (By.ID, "nim")
SEMESTER_DROPDOWN = (By.ID, "semester")
ANGKATAN_DROPDOWN = (By.ID, "angkatan")
# --- Upload Foto ---
FILE_INPUT = (By.ID, "profile_image")
PROFILE_IMAGE = (By.ID, "uploadedAvatar")
RESET_BUTTON = (By.XPATH, "//a[.//span[text()='Reset']]")
# --- Buttons ---
SAVE_BUTTON = (By.XPATH, "//button[contains(text(),'Simpan Perubahan')]")
CANCEL_BUTTON = (By.XPATH, "//button[contains(text(),'Batal')]")
# --- Error Messages ---
ERROR_NAMA = (By.ID, "nama_lengkap-error")
ERROR_NIM = (By.ID, "nim-error")
ERROR_SEMESTER = (By.ID, "semester-error")
ERROR_ANGKATAN = (By.ID, "angkatan-error")
# --- Success Message ---
SUCCESS_ALERT = (By.CLASS_NAME, "alert-success")
# ===============================
# PROFILE DISPLAY METHODS
# ===============================
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
def get_email(self):
return self.driver.find_element(*self.EMAIL_FIELD).get_attribute("value")
def is_email_readonly(self):
return self.driver.find_element(*self.EMAIL_FIELD).get_attribute("readonly") is not None
def get_nama_lengkap(self):
return self.driver.find_element(*self.NAMA_LENGKAP_FIELD).get_attribute("value")
def get_nim(self):
return self.driver.find_element(*self.NIM_FIELD).get_attribute("value")
def get_selected_semester(self):
select = Select(self.driver.find_element(*self.SEMESTER_DROPDOWN))
return select.first_selected_option.text
def get_selected_angkatan(self):
select = Select(self.driver.find_element(*self.ANGKATAN_DROPDOWN))
return select.first_selected_option.text
def get_all_semester_options(self):
"""
Mengambil seluruh opsi semester dari dropdown
Return: list of string (contoh: ["1","2","3","4","5","6","7","8"])
"""
select_element = Select(self.driver.find_element(*self.SEMESTER_DROPDOWN))
options = select_element.options
return [option.text.strip() for option in options]
def get_all_angkatan_options(self):
"""
Mengambil seluruh opsi angkatan dari dropdown
Return: list of string (contoh: ["2020","2021","3","4","5","6","7","8"])
"""
select_element = Select(self.driver.find_element(*self.ANGKATAN_DROPDOWN))
options = select_element.options
return [option.text.strip() for option in options]
# ===============================
# EDIT METHODS
# ===============================
def set_nama_lengkap(self, name):
field = self.driver.find_element(*self.NAMA_LENGKAP_FIELD)
field.clear()
field.send_keys(name)
def set_nim(self, nim):
field = self.driver.find_element(*self.NIM_FIELD)
field.clear()
field.send_keys(nim)
def select_semester(self, value):
Select(self.driver.find_element(*self.SEMESTER_DROPDOWN)).select_by_value(value)
def select_angkatan(self, value):
Select(self.driver.find_element(*self.ANGKATAN_DROPDOWN)).select_by_value(value)
# ===============================
# UPLOAD METHODS
# ===============================
def upload_photo(self, file_path):
self.driver.find_element(*self.FILE_INPUT).send_keys(file_path)
def get_profile_image_src(self):
return self.driver.find_element(*self.PROFILE_IMAGE).get_attribute("src")
def click_reset_photo(self):
wait = WebDriverWait(self.driver, 10)
reset_btn = wait.until(
EC.element_to_be_clickable(self.RESET_BUTTON)
)
self.driver.execute_script("arguments[0].scrollIntoView(true);", reset_btn)
self.driver.execute_script("arguments[0].click();", reset_btn)
# ===============================
# BUTTON ACTIONS
# ===============================
def click_save(self):
wait = WebDriverWait(self.driver, 10)
save_btn = wait.until(
EC.element_to_be_clickable(self.SAVE_BUTTON)
)
# scroll dulu supaya tidak ketutup elemen
self.driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", save_btn)
wait.until(EC.element_to_be_clickable(self.SAVE_BUTTON))
save_btn.click()
def click_cancel(self):
cancel_btn = WebDriverWait(self.driver, 10).until(
EC.element_to_be_clickable(self.CANCEL_BUTTON)
)
self.driver.execute_script("arguments[0].scrollIntoView(true);", cancel_btn)
cancel_btn.click()
# ===============================
# UTILITIES
# ===============================
def refresh_page(self):
self.driver.refresh()
def wait_until_page_loaded(self):
self.wait.until(
EC.presence_of_element_located(self.EMAIL_FIELD)
)
# ===============================
# VALIDATION MESSAGE METHODS
# ===============================
def get_error_nama(self):
return self.wait.until(
EC.visibility_of_element_located(self.ERROR_NAMA)
).text
def get_error_nim(self):
return self.wait.until(
EC.visibility_of_element_located(self.ERROR_NIM)
).text
def get_error_semester(self):
return self.wait.until(
EC.visibility_of_element_located(self.ERROR_SEMESTER)
).text
def get_error_angkatan(self):
return self.wait.until(
EC.visibility_of_element_located(self.ERROR_ANGKATAN)
).text
def wait_until_reload_after_save(self):
self.wait.until(
EC.presence_of_element_located(self.EMAIL_FIELD)
)

202
pages/register_page.py Normal file
View File

@ -0,0 +1,202 @@
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class RegisterPage:
#URL = "https://hypermedialearning.sanggadewa.my.id/register"
URL = "https://hypermedialearning.project2025.id/public/register"
# ===============================
# LOCATORS
# ===============================
NAMA_LENGKAP = (By.ID, "nama_lengkap")
NIM = (By.ID, "nim")
SEMESTER = (By.ID, "semester")
ANGKATAN = (By.ID, "angkatan")
EMAIL = (By.ID, "email")
PASSWORD = (By.NAME, "password")
KONFIRMASI_PASSWORD = (By.NAME, "password_confirmation")
BUTTON_DAFTAR = (By.XPATH, "//button[@type='submit']")
ERROR_MESSAGE = (By.CLASS_NAME, "invalid-feedback")
EMAIL_ERROR_MESSAGE = (
By.XPATH,
"//*[contains(text(),'Email harus menggunakan domain')]"
)
# ===============================
# INIT
# ===============================
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
# ===============================
# PAGE ACTIONS
# ===============================
def open(self):
self.driver.get(self.URL)
def fill_nama_lengkap(self, value: str):
field = self.wait.until(
EC.visibility_of_element_located(self.NAMA_LENGKAP)
)
field.clear()
field.send_keys(value)
def fill_nim(self, value: str):
field = self.wait.until(
EC.visibility_of_element_located(self.NIM)
)
field.clear()
field.send_keys(value)
def fill_semester(self, value: str):
field = self.wait.until(
EC.visibility_of_element_located(self.SEMESTER)
)
field.clear()
field.send_keys(value)
def fill_angkatan(self, value: str):
field = self.wait.until(
EC.visibility_of_element_located(self.ANGKATAN)
)
field.clear()
field.send_keys(value)
def fill_email(self, value: str):
field = self.wait.until(
EC.visibility_of_element_located(self.EMAIL)
)
field.clear()
field.send_keys(value)
def fill_password(self, value: str):
field = self.wait.until(
EC.visibility_of_element_located(self.PASSWORD)
)
field.clear()
field.send_keys(value)
def fill_confirm_password(self, value: str):
field = self.wait.until(
EC.visibility_of_element_located(self.KONFIRMASI_PASSWORD)
)
field.clear()
field.send_keys(value)
def click_daftar(self):
self.wait.until(
EC.element_to_be_clickable(self.BUTTON_DAFTAR)
).click()
# ===============================
# HELPER / BUSINESS METHODS
# ===============================
def fill_form(self, data: dict):
self.fill_nama_lengkap(data["nama_lengkap"])
self.fill_nim(data["nim"])
self.fill_semester(data["semester"])
self.fill_angkatan(data["angkatan"])
self.fill_email(data["email"])
self.fill_password(data["password"])
if "konfirmasi_password" in data:
self.fill_confirm_password(data["konfirmasi_password"])
pwd = self.driver.find_element(*self.PASSWORD).get_attribute("value")
cpwd = self.driver.find_element(*self.KONFIRMASI_PASSWORD).get_attribute("value")
print("PASSWORD FIELD :", repr(pwd))
print("CONFIRM FIELD :", repr(cpwd))
def submit(self):
"""Alias agar test lebih readable"""
self.click_daftar()
def get_error_messages(self):
elements = self.driver.find_elements(*self.ERROR_MESSAGE)
return [el.text for el in elements if el.text.strip()]
def has_error(self, field_name: str) -> bool:
"""
Mengecek apakah error message terkait field tertentu muncul
"""
return any(
field_name.lower() in err.lower()
for err in self.get_error_messages()
)
def is_email_domain_error_displayed(self) -> bool:
try:
return self.driver.find_element(
*self.EMAIL_ERROR_MESSAGE
).is_displayed()
except Exception:
return False
def is_register_success(self) -> bool:
"""
Register dianggap sukses jika terjadi redirect dari halaman register.
"""
self.wait.until(
lambda d: d.current_url != self.URL
)
return True
def is_field_required(self, label_text: str) -> bool:
"""
Mengecek apakah field dengan label tertentu memiliki atribut required
(HTML5 native validation)
"""
fields = {
"nama_lengkap": self.NAMA_LENGKAP,
"NIM": self.NIM,
"nim": self.NIM,
"semester": self.SEMESTER,
"angkatan": self.ANGKATAN,
"email": self.EMAIL,
"password": self.PASSWORD,
"konfirmasi_password":self.KONFIRMASI_PASSWORD,
}
locator = fields.get(label_text)
if not locator:
raise ValueError(f"Field '{label_text}' tidak dikenali")
element = self.driver.find_element(*locator)
return element.get_attribute("required") is not None
def get_value(self, field_name: str) -> str:
field = self.get_field(field_name)
return field.get_attribute("value")
def get_field(self, field_name: str):
fields = {
"nama_lengkap": self.NAMA_LENGKAP,
"NIM": self.NIM,
"nim": self.NIM,
"semester": self.SEMESTER,
"Semester": self.SEMESTER,
"angkatan": self.ANGKATAN,
"Angkatan": self.ANGKATAN,
"email": self.EMAIL,
"password": self.PASSWORD,
"konfirmasi_password":self.KONFIRMASI_PASSWORD,
}
locator = fields.get(field_name)
if not locator:
raise ValueError(f"Field '{field_name}' tidak dikenali")
return self.driver.find_element(*locator)
def has_html5_validation(self, field_name: str) -> bool:
field = self.get_field(field_name)
return field.get_attribute("validationMessage") != ""

View File

@ -0,0 +1,45 @@
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
class ResumePembelajaranPage:
# ===== Locator =====
PAGE_TITLE = (By.XPATH, "//h4[normalize-space()='Resume Pembelajaran']")
EMPTY_MESSAGE = (
By.CSS_SELECTOR,
"div.text-center.text-muted"
)
RESUME_CARD = (By.CSS_SELECTOR, "div.card")
RESUME_CONTENT = (By.CSS_SELECTOR, "div.card-body pre")
def __init__(self, driver):
self.driver = driver
self.wait = WebDriverWait(driver, 10)
# ===== Assertions / Getters =====
def page_loaded(self):
self.wait.until(EC.visibility_of_element_located(self.PAGE_TITLE))
def is_empty_message_displayed(self):
try:
self.wait.until(
EC.presence_of_element_located(self.EMPTY_MESSAGE)
)
return True
except TimeoutException:
return False
def is_resume_displayed(self):
return self.wait.until(
EC.visibility_of_element_located(self.RESUME_CARD)
)
def get_resume_text(self):
return self.wait.until(
EC.visibility_of_element_located(self.RESUME_CONTENT)
).text

4
pytest.ini Normal file
View File

@ -0,0 +1,4 @@
[pytest]
addopts = -v -s
testpaths = tests
markers = dashboard: test untuk fitur dashboard

16
requirements.txt Normal file
View File

@ -0,0 +1,16 @@
selenium
pytest
webdriver-manager
.\venv\Scripts\Activate.ps1 (perintah aktifkan venv)
def fill_email(self, email):
email_field = self.driver.find_element(*self.EMAIL)
email_field.clear()
email_field.send_keys(email)
def fill_required_fields_except_email(self):
self.fill_nama("Mahasiswa Polije")
self.fill_password("Password123!")
self.fill_konfirmasi_password("Password123!")

View File

@ -0,0 +1,25 @@
import pytest
from pages.dashboard_page import DashboardPage
@pytest.mark.dashboard
class TestDashboardPengisianKuesioner:
def test_section_pengisian_kuesioner_tampil(self, driver, login_as_user_sudah_kuesioner):
dashboard = DashboardPage(driver)
dashboard.open()
assert dashboard.is_pengisian_kuesioner_section_visible()
def test_kuesioner_vark_mai_tampil(self, driver, login_as_user_sudah_kuesioner):
dashboard = DashboardPage(driver)
dashboard.open()
assert dashboard.is_kuesioner_vark_mai_visible()
def test_click_isi_kuesioner_redirect(self, driver, login_as_user_sudah_kuesioner):
dashboard = DashboardPage(driver)
dashboard.open()
dashboard.click_isi_kuesioner()
assert "kuesioner" in driver.current_url

View File

@ -0,0 +1,30 @@
import pytest
from pages.dashboard_page import DashboardPage
@pytest.mark.dashboard
class TestDashboardPopup:
def test_popup_muncul_jika_belum_isi_kuesioner(self, driver, login_as_user_belum_kuesioner2):
dashboard = DashboardPage(driver)
dashboard.open()
assert dashboard.is_popup_visible()
def test_button_isi_kuesioner_redirect(self,
driver, login_as_user_belum_kuesionerNew2):
dashboard = DashboardPage(driver)
dashboard.open()
dashboard.click_isi_kuesioner_pop_up()
assert dashboard.is_redirected_to_kuesioner()
def test_button_close_x_menutup_popup(self, driver, login_as_user_belum_kuesioner2):
dashboard = DashboardPage(driver)
dashboard.open()
dashboard.close_popup_with_x()
dashboard.wait_popup_disappear()
assert not dashboard.is_popup_visible()

View File

@ -0,0 +1,48 @@
import pytest
from pages.dashboard_page import DashboardPage
@pytest.mark.dashboard
class TestDashboardStatusKMRM:
def test_status_km_belum_diisi(self, driver, login_as_user_belum_kuesioner2):
dashboard = DashboardPage(driver)
dashboard.open()
dashboard.close_kuesioner_popup_if_present()
print("KM TEXT =", dashboard.get_km_status_text())
assert dashboard.is_km_not_filled()
def test_status_rm_belum_diisi(self, driver, login_as_user_belum_kuesioner2):
dashboard = DashboardPage(driver)
dashboard.open()
dashboard.close_kuesioner_popup_if_present()
print("RM TEXT =", dashboard.get_rm_status_text())
assert dashboard.is_rm_not_filled()
def test_status_km_sudah_isi_kuesioner(self, driver, login_as_user_sudah_kuesioner):
dashboard = DashboardPage(driver)
assert dashboard.is_km_filled()
print("KM TEXT =", dashboard.get_km_status_text())
def test_status_rm_sudah_isi_kuesioner(self, driver, login_as_user_sudah_kuesioner):
dashboard = DashboardPage(driver)
assert dashboard.is_rm_filled()
print("RM TEXT =", dashboard.get_rm_status_text())
def test_learning_style_belum_diisi(self, driver, login_as_user_belum_kuesioner2):
dashboard = DashboardPage(driver)
dashboard.open()
dashboard.close_kuesioner_popup_if_present()
assert dashboard.is_learning_style_not_filled()
print("LEARNING STYLE =", dashboard.get_learning_style_text())
def test_learning_style_sudah_diisi(self,
driver, login_as_user_sudah_kuesioner3):
dashboard = DashboardPage(driver)
dashboard.open()
assert dashboard.is_learning_style_filled()
print("LEARNING STYLE =", dashboard.get_learning_style_text())

View File

@ -0,0 +1,107 @@
import re
import pytest
from pages.history_kuesioner_page import HistoryKuesionerPage
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
@pytest.mark.usefixtures("login_as_user_belum_kuesioner")
class TestHistoryKuesioner:
def test_history_page_loaded(self, driver):
page = HistoryKuesionerPage(driver)
page.open()
assert page.is_page_loaded()
def test_km_dan_rm_value(self, driver):
page = HistoryKuesionerPage(driver)
page.open()
assert page.get_km_value() in ["low", "medium", "high"]
assert page.get_rm_value() in ["low", "medium", "high"]
def test_ringkasan_skor_gaya_belajar(self, driver):
"""
PASSED berarti:
- 4 gaya belajar muncul
- Label sesuai requirement
- Nilai skor valid (angka)
"""
page = HistoryKuesionerPage(driver)
page.open()
scores = page.get_scores()
expected_labels = {"Visual", "Auditory", "Read/Write", "Kinesthetic"}
assert set(scores.keys()) == expected_labels, (
f"Label tidak sesuai: {scores.keys()}"
)
for label, value in scores.items():
assert value.isdigit(), f"Skor {label} bukan angka: {value}"
def test_progress_pengisian_100_persen(self, driver):
page = HistoryKuesionerPage(driver)
page.open()
progress = page.get_progress_value()
assert progress == "100", f"Progress seharusnya 100%, tetapi {progress}%"
def test_gaya_belajar_dominan_valid(self, driver):
page = HistoryKuesionerPage(driver)
page.open()
dominant = page.get_dominant_style()
assert dominant in ["Visual", "Auditory", "Read/Write", "Kinesthetic"], (
f"Gaya dominan tidak valid: {dominant}"
)
def test_button_mulai_belajar_redirect(self, driver):
page = HistoryKuesionerPage(driver)
page.open()
page.click_mulai()
WebDriverWait(driver, 10).until(
lambda d: "materi" in d.current_url
)
assert "materi" in driver.current_url
def test_tabel_riwayat_memuat_data(self, driver):
page = HistoryKuesionerPage(driver)
page.open()
rows = page.get_history_rows()
assert len(rows) > 0, "Tabel riwayat kosong"
def test_format_tanggal_valid(self, driver):
page = HistoryKuesionerPage(driver)
page.open()
row_text = page.get_history_rows()[0].text
assert re.search(r"\d{2}\s[A-Za-z]{3}\s\d{4}", row_text), (
f"Format tanggal tidak valid: {row_text}"
)
def test_button_ubah_redirect(self, driver):
page = HistoryKuesionerPage(driver)
page.open()
page.click_ubah()
WebDriverWait(driver, 5).until(EC.url_contains("kuesioner"))
assert "kuesioner" in driver.current_url
def test_button_unduh_redirect(self, driver):
"""
PASSED berarti:
- Tombol bisa diklik
- Redirect ke halaman user-result berhasil
"""
page = HistoryKuesionerPage(driver)
page.open()
page.click_unduh()
WebDriverWait(driver, 5).until(EC.url_contains("user-result"))
assert "user-result" in driver.current_url

View File

@ -0,0 +1,50 @@
import pytest
from pages.kuesioner_ls_page import KuesionerLSPage
class TestKuesionerLS:
def test_open_kuesioner_ls(self, driver, login_as_user_belum_kuesioner):
page = KuesionerLSPage(driver)
page.open_from_sidebar()
page.click_mulai_kuesioner()
assert "kuesioner-ls" in driver.current_url
def test_submit_without_answer_should_fail(self, driver, login_as_user_belum_kuesioner):
page = KuesionerLSPage(driver)
page.open_from_sidebar()
page.click_mulai_kuesioner()
# jawab hanya 15 dari 16
for i in range(1, 16):
page.answer_question(i)
page.submit()
assert "kuesioner-ls" in driver.current_url
def test_submit_success_redirect_to_mai(self, driver, login_as_user_belum_kuesioner):
page = KuesionerLSPage(driver)
page.open_from_sidebar()
page.click_mulai_kuesioner()
page.answer_all_questions()
page.submit()
assert "kuesioner-mai" in driver.current_url
def test_force_navigation_before_submit_allowed_known_issue(
self, driver, login_as_user_belum_kuesioner
):
page = KuesionerLSPage(driver)
page.open_from_sidebar()
page.click_mulai_kuesioner()
# jawab sebagian (belum selesai)
page.answer_question(1)
page.answer_question(2)
# user paksa pindah halaman
page.force_navigate_to_dashboard()
assert "kuesioner" in driver.current_url

View File

@ -0,0 +1,40 @@
import pytest
from pages.kuesioner_mai_page import KuesionerMAIPage
class TestKuesionerMAI:
def test_open_kuesioner_mai(self, driver, login_as_user_belum_kuesioner):
page = KuesionerMAIPage(driver)
page.open_from_sidebar()
page.click_mulai_kuesioner()
page.answer_all_questions_ls()
page.submit()
assert "kuesioner-mai" in driver.current_url
def test_submit_mai_without_answer_should_fail(self, driver, login_as_user_belum_kuesioner):
page = KuesionerMAIPage(driver)
page.open_from_sidebar()
page.click_mulai_kuesioner()
page.answer_all_questions_ls()
page.submit()
# jawab hanya 50 dari 52
for i in range(2, 52):
page.answer_question_mai(i)
page.submit()
# tetap di halaman MAI
assert "kuesioner-mai" in driver.current_url
def test_submit_mai_success_redirect_to_history(self, driver, login_as_user_belum_kuesioner):
page = KuesionerMAIPage(driver)
page.open_from_sidebar()
page.click_mulai_kuesioner()
page.answer_all_questions_ls()
page.submit()
page.answer_all_questions_mai()
page.submit()
assert "history_quis" in driver.current_url

View File

@ -0,0 +1,30 @@
import pytest
from pages.kuesioner_panduan_page import KuesionerPanduanPage
class TestKuesionerPanduan:
def test_open_panduan_from_sidebar(self,
driver, login_as_user_sudah_kuesioner):
panduan = KuesionerPanduanPage(driver)
panduan.open_from_sidebar()
assert panduan.is_on_panduan_page()
def test_click_mulai_kuesioner_redirect(self, driver, login_as_user_sudah_kuesioner):
panduan = KuesionerPanduanPage(driver)
panduan.open_from_sidebar()
panduan.click_mulai_kuesioner()
assert "kuesioner-ls" in driver.current_url
def test_click_bantuan_redirect_whatsapp(self, driver, login_as_user_sudah_kuesioner):
panduan = KuesionerPanduanPage(driver)
panduan.open_from_sidebar()
panduan.click_bantuan()
current_url = driver.current_url
assert "whatsapp.com" in current_url
assert "6285290543351" in current_url

View File

@ -0,0 +1,72 @@
import pytest
@pytest.mark.usefixtures("driver")
class TestLoginEmail:
"""
TEST LOGIN - EMAIL FIELD
"""
VALID_EMAIL = "e41222052@student.polije.ac.id"
VALID_PASSWORD = "e41222052@student.polije.ac.id"
# =========================
# POSITIVE TEST
# =========================
def test_login_email_valid(self, login_page):
login_page.open()
login_page.login(
self.VALID_EMAIL,
self.VALID_PASSWORD
)
assert login_page.is_login_success()
# =========================
# NEGATIVE TESTS
# =========================
def test_login_email_salah_password_benar(self, login_page):
login_page.open()
login_page.login(
"salah@student.polije.ac.id",
self.VALID_PASSWORD
)
assert login_page.has_global_error()
def test_login_email_kosong(self, login_page):
login_page.open()
login_page.fill_email("")
login_page.fill_password(self.VALID_PASSWORD)
login_page.click_login()
assert login_page.is_field_required("email")
def test_login_email_tanpa_at(self, login_page):
login_page.open()
login_page.login(
"e41222052student.polije.ac.id",
self.VALID_PASSWORD
)
assert login_page.get_email_validation_message()
def test_login_email_hanya_spasi(self, login_page):
login_page.open()
login_page.login(
" ",
self.VALID_PASSWORD
)
assert login_page.get_email_validation_message()
def test_login_email_tidak_terdaftar(self, login_page):
login_page.open()
login_page.login(
"tidakterdaftar@student.polije.ac.id",
self.VALID_PASSWORD
)
assert login_page.has_global_error()

View File

@ -0,0 +1,50 @@
import pytest
class TestLoginPassword:
VALID_EMAIL = "e41222052@student.polije.ac.id"
VALID_PASSWORD = "e41222052@student.polije.ac.id"
# ==========================
# PASSWORD NEGATIVE CASES
# ==========================
def test_password_kosong(self, login_page):
login_page.fill_email(self.VALID_EMAIL)
login_page.fill_password("")
login_page.submit()
assert login_page.has_html5_validation("password")
def test_password_salah(self, login_page):
login_page.fill_email(self.VALID_EMAIL)
login_page.fill_password("PasswordSalah123!")
login_page.submit()
assert login_page.is_login_failed()
def test_password_hanya_spasi(self, login_page):
login_page.fill_email(self.VALID_EMAIL)
login_page.fill_password(" ")
login_page.submit()
assert login_page.is_login_failed()
def test_password_kurang_dari_8_karakter(self, login_page):
login_page.fill_email(self.VALID_EMAIL)
login_page.fill_password("Abc1!")
login_page.submit()
assert login_page.is_login_failed()
# ==========================
# PASSWORD POSITIVE CASE
# ==========================
def test_password_valid(self, login_page):
login_page.fill_email(self.VALID_EMAIL)
login_page.fill_password(self.VALID_PASSWORD)
login_page.submit()
assert login_page.is_login_success()

View File

@ -0,0 +1,28 @@
import pytest
@pytest.mark.usefixtures("driver")
class TestLoginRememberMe:
VALID_EMAIL = "e41222052@student.polije.ac.id"
VALID_PASSWORD = "e41222052@student.polije.ac.id"
def test_remember_me_checkbox_dapat_dicentang(self, login_page):
login_page.fill_email(self.VALID_EMAIL)
login_page.fill_password(self.VALID_PASSWORD)
login_page.click_remember_me()
assert login_page.is_remember_me_checked()
def test_login_dengan_remember_me_dan_refresh(self, login_page, driver):
login_page.fill_email(self.VALID_EMAIL)
login_page.fill_password(self.VALID_PASSWORD)
login_page.click_remember_me()
login_page.submit()
assert login_page.is_login_success()
# refresh halaman
driver.refresh()
# masih di dashboard
assert login_page.is_login_success()

View File

@ -0,0 +1,13 @@
import pytest
class TestLoginSSOGoogle:
def test_button_sso_google_tersedia(self, login_page):
assert login_page.driver.find_element(
*login_page.SSO_GOOGLE_BUTTON
)
def test_redirect_ke_google_oauth(self, login_page):
login_page.click_sso_google()
assert login_page.is_redirected_to_google()

View File

@ -0,0 +1,46 @@
import pytest
from pages.logout_page import LogoutPage
class TestLogout:
# ===============================
# 1⃣ Test: Modal Muncul
# ===============================
def test_logout_modal_muncul(self, driver, login_as_user_sudah_kuesioner):
logout = LogoutPage(driver)
logout.open_user_dropdown()
logout.click_logout_menu()
assert logout.is_logout_modal_visible()
# ===============================
# 2⃣ Test: Klik Tidak → Tetap di halaman
# ===============================
def test_logout_tidak_tetap_di_dashboard(self, driver, login_as_user_sudah_kuesioner):
logout = LogoutPage(driver)
current_url = driver.current_url
logout.open_user_dropdown()
logout.click_logout_menu()
logout.click_tidak()
assert driver.current_url == current_url
# ===============================
# 3⃣ Test: Klik Ya → Redirect ke Home
# ===============================
def test_logout_ya_redirect_ke_home(self, driver, login_as_user_sudah_kuesioner):
logout = LogoutPage(driver)
logout.open_user_dropdown()
logout.click_logout_menu()
logout.click_ya_logout()
logout.wait_until_redirect_to_home()
assert "dashboard" not in driver.current_url.lower()

View File

@ -0,0 +1,46 @@
from pages.materi_page import MateriPage
class TestMateriAuditory:
def test_open_auditory_material(self, driver, login_as_user_sudah_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/materi/auditory/1")
page = MateriPage(driver)
page.auditory_page_loaded()
page.sidebar_visible()
assert "materi/auditory" in driver.current_url
def test_auditory_has_audio_player(self, driver, login_as_user_sudah_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/materi/auditory/1")
page = MateriPage(driver)
page.auditory_page_loaded()
audio = page.driver.find_element(*page.AUDIO_PLAYER)
assert audio.is_displayed()
assert audio.get_attribute("controls") is not None
def test_navigate_auditory_to_visual(self, driver, login_as_user_sudah_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/materi/auditory/1")
page = MateriPage(driver)
page.go_to_visual()
page.visual_page_loaded()
assert "materi/visual" in driver.current_url
def test_navigation_requires_confirmation_from_auditory(
self, driver, login_as_user_sudah_kuesioner
):
driver.get("https://hypermedialearning.sanggadewa.my.id/materi/auditory/1")
page = MateriPage(driver)
# User mencoba pindah sebelum selesai belajar
page.go_to_visual()
# Expected behavior (ideal requirement):
assert "materi/auditory" in driver.current_url, \
"User seharusnya tidak bisa meninggalkan halaman sebelum selesai belajar"

View File

@ -0,0 +1,141 @@
import pytest
from pages.live_coding_page import LiveCodingPage
class TestKinesthetic:
def test_live_coding_page_loaded(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/materi/kinesthetic/1")
page = LiveCodingPage(driver)
assert page.is_page_loaded()
assert page.editor_is_visible()
assert "Hasil Output" in page.get_expected_output_text()
def test_submit_without_completing_code(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/materi/kinesthetic/1")
page = LiveCodingPage(driver)
page.submit_code()
result = page.get_result_message()
assert "Kompilasi Gagal" in result
"""assert "Jawaban Salah" in result
assert "Seharusnya" in result"""
def test_submit_with_wrong_code(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/materi/kinesthetic/1")
page = LiveCodingPage(driver)
page.append_code("System.out.println(\"SALAH\");")
page.submit_code()
result = page.get_result_message()
assert "Kompilasi Gagal" in result
"""assert "Jawaban Salah" in result
assert "Seharusnya" in result"""
def test_submit_with_correct_code(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/materi/kinesthetic/1")
page = LiveCodingPage(driver)
page.append_code(
"System.out.println(m1.getNama() + \" | IPK: \" + m1.getIpk() + \"\\n\" + m2.getNama() + \" | IPK: \" + m2.getIpk());")
page.submit_code()
result = page.get_result_message()
assert "Jawaban Benar" in result
def test_default_code_is_displayed(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/materi/kinesthetic/1")
page = LiveCodingPage(driver)
code = page.get_editor_value()
assert code.strip() != "", \
"FAIL: Kode bawaan materi tidak tampil pada editor"
def test_popup_can_be_closed(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/materi/kinesthetic/1")
page = LiveCodingPage(driver)
page.append_code("\nprint('test')")
page.submit_code()
page.wait_popup_visible()
assert page.is_popup_visible()
page.close_result_modal()
page.wait_popup_invisible()
assert not page.is_popup_visible(), \
"FAIL: Popup evaluasi tidak tertutup"
def test_previous_button_on_first_page(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/materi/kinesthetic/1")
page = LiveCodingPage(driver)
initial_url = page.get_current_url()
page.click_previous()
current_url = page.get_current_url()
assert current_url == initial_url, (
"FAIL: Tombol Sebelumnya pada halaman pertama "
"seharusnya tidak berpindah halaman"
)
assert not page.is_404_page(), (
"FAIL: Tombol Sebelumnya menyebabkan halaman 404"
)
def test_next_button_on_first_page(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/materi/kinesthetic/1")
page = LiveCodingPage(driver)
initial_url = page.get_current_url()
page.click_next()
current_url = page.get_current_url()
assert current_url != initial_url, (
"FAIL: Tombol Selanjutnya tidak berpindah ke halaman berikutnya"
)
assert not page.is_404_page(), (
"FAIL: Tombol Selanjutnya menyebabkan halaman 404"
)
def test_navigation_without_submit_shows_validation(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/materi/kinesthetic/1")
page = LiveCodingPage(driver)
"""
User berpindah halaman sebelum submit kode
Expected: muncul notifikasi validasi
"""
initial_url = page.get_current_url()
page.append_code("\nSystem.out.println('Belum submit');")
page.click_other_menu()
# ===== CASE 1: JS ALERT =====
alert_text = page.is_confirm_alert_present()
if alert_text:
assert (
"yakin" in alert_text.lower()
or "belum" in alert_text.lower()
), "FAIL: Teks alert tidak sesuai validasi"
return
# ===== CASE 2: HTML MODAL =====
assert page.is_confirm_modal_visible(), (
"FAIL: Tidak ada alert atau modal validasi saat berpindah halaman"
)
# Pastikan belum pindah halaman
assert page.get_current_url() == initial_url, (
"FAIL: Sistem berpindah halaman tanpa konfirmasi"
)

View File

@ -0,0 +1,55 @@
import pytest
from pages.materi_pembelajaran_page import MateriPembelajaranPage
class TestMateriPembelajaranEnkapsulasi:
def test_open_halaman_enkapsulasi(self, driver, login_as_user_sudah_kuesioner):
page = MateriPembelajaranPage(driver)
page.open_from_sidebar()
assert "/materi" in driver.current_url
def test_tampilkan_learning_style_user(self, driver, login_as_user_sudah_kuesioner):
page = MateriPembelajaranPage(driver)
page.open_from_sidebar()
style = page.get_learning_style_user()
assert style in ["Visual", "Auditory", "Read/Write", "Kinesthetic"]
def test_mulai_belajar_learning_style_utama(self, driver, login_as_user_sudah_kuesioner):
page = MateriPembelajaranPage(driver)
page.open_from_sidebar()
page.click_mulai_belajar_utama()
judul = page.get_judul_materi()
assert judul != ""
def test_pesan_materi_alternatif_muncul(self, driver, login_as_user_sudah_kuesioner):
page = MateriPembelajaranPage(driver)
page.open_from_sidebar()
assert page.wait.until(
lambda d: d.find_element(*page.PESAN_ALTERNATIF)
)
def test_mulai_belajar_visual(self, driver, login_as_user_sudah_kuesioner):
page = MateriPembelajaranPage(driver)
page.open_from_sidebar()
page.click_visual()
assert page.get_judul_materi() == "Materi Visual"
def test_mulai_belajar_auditory(self, driver, login_as_user_sudah_kuesioner):
page = MateriPembelajaranPage(driver)
page.open_from_sidebar()
page.click_auditory()
assert page.get_judul_materi() == "Materi Auditory"
def test_mulai_belajar_read_write(self, driver, login_as_user_sudah_kuesioner):
page = MateriPembelajaranPage(driver)
page.open_from_sidebar()
page.click_read_write()
assert page.get_judul_materi() == "Materi Read / Write"

View File

@ -0,0 +1,114 @@
import pytest
from pages.materi_readwrite_page import MateriReadWritePage
from selenium.webdriver.support import expected_conditions as EC
class TestMateriReadWrite:
def test_readwrite_page_display_text_materi(self, driver, login_as_user_sudah_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/materi/readwrite/1")
page = MateriReadWritePage(driver)
page.page_loaded()
text = page.get_text_materi()
assert len(text) > 0
def test_submit_rangkuman_success(self, driver, login_as_user_sudah_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/materi/readwrite/1")
page = MateriReadWritePage(driver)
page.page_loaded()
rangkuman_valid = (
"Enkapsulasi adalah konsep dalam pemrograman berorientasi objek "
"yang digunakan untuk membungkus data dan method dalam satu kelas. "
"Tujuannya adalah melindungi data agar tidak diakses langsung dari luar."
)
page.input_rangkuman(rangkuman_valid)
page.submit_rangkuman()
alert = page.success_message_displayed()
assert "berhasil" in alert.text.lower()
def test_submit_rangkuman_empty_should_fail(self, driver, login_as_user_sudah_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/materi/readwrite/1")
page = MateriReadWritePage(driver)
page.page_loaded()
page.input_rangkuman("")
page.submit_rangkuman()
# tetap di halaman yang sama (validasi HTML required)
assert "readwrite" in driver.current_url
def test_tugas_rangkuman_section_label_displayed(self, driver, login_as_user_sudah_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/materi/readwrite/1")
page = MateriReadWritePage(driver)
page.page_loaded()
label = page.wait.until(
EC.visibility_of_element_located(page.TUGAS_RANGKUMAN_LABEL)
)
assert label.is_displayed()
assert "TUGAS RANGKUMAN" in label.text
def test_input_rangkuman_textarea_displayed(self, driver, login_as_user_sudah_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/materi/readwrite/1")
page = MateriReadWritePage(driver)
page.page_loaded()
textarea = page.wait.until(
EC.visibility_of_element_located(page.RANGKUMAN_TEXTAREA)
)
assert textarea.is_displayed()
assert textarea.is_enabled()
def test_submit_rangkuman_less_than_50_words_should_be_rejected(self, driver, login_as_user_sudah_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/materi/readwrite/1")
page = MateriReadWritePage(driver)
page.page_loaded()
short_text = "Enkapsulasi adalah konsep OOP untuk melindungi data."
page.input_rangkuman(short_text)
page.submit_rangkuman()
assert page.error_message_displayed()
def test_submit_rangkuman_more_than_75_words_should_be_rejected(self, driver, login_as_user_sudah_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/materi/readwrite/1")
page = MateriReadWritePage(driver)
page.page_loaded()
long_text = " ".join(["enkapsulasi"] * 80)
page.input_rangkuman(long_text)
page.submit_rangkuman()
assert page.error_message_displayed()
def test_cannot_leave_page_without_submit_rangkuman(self, driver, login_as_user_sudah_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/materi/readwrite/1")
page = MateriReadWritePage(driver)
page.page_loaded()
page.input_rangkuman("Rangkuman belum dikirim")
page.go_to_visual()
# Sistem HARUS menahan user di halaman ini
assert "readwrite" in driver.current_url
def test_textarea_cleared_after_submit(self, driver, login_as_user_sudah_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/materi/readwrite/1")
page = MateriReadWritePage(driver)
page.page_loaded()
text = " ".join(["enkapsulasi"] * 50)
page.input_rangkuman(text)
page.submit_rangkuman()
textarea = driver.find_element(*page.RANGKUMAN_TEXTAREA)
assert textarea.get_attribute("value") == ""

View File

@ -0,0 +1,41 @@
import pytest
from pages.materi_page import MateriPage
class TestMateriVisual:
def test_open_visual_material(self, driver, login_as_user_sudah_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/materi/visual/1")
page = MateriPage(driver)
page.visual_page_loaded()
page.sidebar_visible()
assert "materi/visual" in driver.current_url
def test_visual_has_image(self, driver, login_as_user_sudah_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/materi/visual/1")
page = MateriPage(driver)
page.visual_page_loaded()
assert page.driver.find_element(*page.VISUAL_IMAGE).is_displayed()
def test_navigate_visual_to_auditory(self, driver, login_as_user_sudah_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/materi/visual/1")
page = MateriPage(driver)
page.go_to_auditory()
page.auditory_page_loaded()
assert "materi/auditory" in driver.current_url
#@pytest.mark.xfail(reason="Belum ada validasi sebelum meninggalkan halaman materi")
def test_navigation_requires_confirmation(self, driver, login_as_user_sudah_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/materi/visual/1")
page = MateriPage(driver)
page.go_to_auditory()
# Expected behavior (yang benar secara requirement)
assert "materi/visual" in driver.current_url

View File

@ -0,0 +1,34 @@
import pytest
from datetime import datetime
from pages.profile_page import ProfilePage
# ======================================================
# ANGKATAN VALIDATION
# ======================================================
class TestProfileAngkatanValidation:
def test_angkatan_dropdown_options(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
current_year = datetime.now().year
expected_years = [
str(current_year - i) for i in reversed(range(7))
]
actual_years = page.get_all_angkatan_options()
assert actual_years == expected_years
def test_angkatan_valid(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
page.select_angkatan("2022")
page.click_save()
page.wait_until_reload_after_save()
assert page.get_selected_angkatan() == "2022"

View File

@ -0,0 +1,31 @@
import pytest
from pages.profile_page import ProfilePage
class TestProfileButton:
def test_button_simpan_update_data(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
new_name = "Ini Testing"
page.set_nama_lengkap(new_name)
page.click_save()
page.wait_until_reload_after_save()
assert page.get_nama_lengkap() == new_name
def test_button_simpan_validasi_gagal_revert(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
default_name = page.get_nama_lengkap()
page.set_nama_lengkap("")
page.click_save()
assert page.get_nama_lengkap() == default_name
def test_button_batal_kembali_ke_default(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
default_name = page.get_nama_lengkap()
page.set_nama_lengkap("Nama Tidak Disimpan")
page.click_cancel()
assert page.get_nama_lengkap() == default_name

View File

@ -0,0 +1,70 @@
import pytest
from pages.profile_page import ProfilePage
class TestProfileDisplay:
def test_email_tampil_sesuai_akun(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
"""
Verifikasi email tampil sesuai akun dan tidak kosong
"""
page.wait_until_page_loaded()
email = page.get_email()
assert email is not None
#assert email != ""
assert "@" in email
assert email.endswith(".ac.id")
#or email.endswith(".com")
def test_email_tidak_bisa_diedit(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
"""
Verifikasi email memiliki attribute readonly
"""
assert page.is_email_readonly() is True
def test_nim_tampil_sesuai_akun(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
"""
Verifikasi NIM tampil dan tidak kosong
"""
nim = page.get_nim()
assert nim is not None
assert nim != ""
assert len(nim) >= 9
assert len(nim) <= 10
def test_semester_tampil_sesuai_akun(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
"""
Verifikasi semester memiliki selected value
"""
semester = page.get_selected_semester()
assert semester is not None
assert semester.isdigit()
assert int(semester) >= 1
assert int(semester) <= 8
def test_angkatan_tampil_sesuai_akun(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
"""
Verifikasi angkatan memiliki selected value
"""
angkatan = page.get_selected_angkatan()
assert angkatan is not None
assert angkatan.isdigit()
assert len(angkatan) == 4

View File

@ -0,0 +1,64 @@
import pytest
from datetime import datetime
from pages.profile_page import ProfilePage
class TestProfileNamaValidation:
# ======================================================
# NAMA VALIDATION
# ======================================================
def test_nama_kosong(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
default_name = page.get_nama_lengkap()
page.set_nama_lengkap("")
page.click_save()
assert page.get_nama_lengkap() == default_name
def test_nama_valid(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
page.set_nama_lengkap("Rurin Nurliza")
page.click_save()
page.wait_until_reload_after_save()
assert page.get_nama_lengkap() == "Rurin Nurliza"
def test_nama_kurang_5_karakter(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
default_name = page.get_nama_lengkap()
page.set_nama_lengkap("Rin")
page.click_save()
assert page.get_nama_lengkap() == default_name, ("FAIL, Sistem tidak mengembalikan nama ke default")
def test_nama_lebih_30_karakter(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
default_name = page.get_nama_lengkap()
page.set_nama_lengkap("IniNamaYangSangatPanjangSekaliSeharusnyaTidakBoleh")
page.click_save()
assert page.get_nama_lengkap() == default_name, ("FAIL, Sistem tidak mengembalikan nama ke default")
def test_nama_angka_semua(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
default_name = page.get_nama_lengkap()
page.set_nama_lengkap("12345678")
page.click_save()
assert page.get_nama_lengkap() == default_name, ("FAIL, Sistem tidak mengembalikan nama ke default dan tidak memberika validasi error")
def test_nama_simbol_semua(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
default_name = page.get_nama_lengkap()
page.set_nama_lengkap("@@@@@@@")
page.click_save()
assert page.get_nama_lengkap() == default_name, ("FAIL, Sistem tidak mengembalikan nama ke default dan tidak memberika validasi error")

View File

@ -0,0 +1,53 @@
import pytest
from datetime import datetime
from pages.profile_page import ProfilePage
class TestProfileNimValidation:
# ======================================================
# NIM VALIDATION
# ======================================================
def test_nim_kosong(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
default_nim = page.get_nim()
page.set_nim("")
page.click_save()
assert page.get_nim() == default_nim
def test_nim_valid(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
page.set_nim("2022012345")
page.click_save()
page.wait_until_reload_after_save()
assert page.get_nim() == "2022012345"
def test_nim_bukan_angka(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
default_nim = page.get_nim
page.set_nim("ABC12345")
page.click_save()
assert page.get_nim() == default_nim
def test_nim_kurang_minimal(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
default_nim = page.get_nim
page.set_nim("1234567")
page.click_save()
assert page.get_nim() == default_nim
def test_nim_lebih_maksimal(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
default_nim = page.get_nim
page.set_nim("1234567890123456")
page.click_save()
assert page.get_nim() == default_nim

View File

@ -0,0 +1,29 @@
import pytest
from datetime import datetime
from pages.profile_page import ProfilePage
class TestProfileSemesterValidation:
# ======================================================
# SEMESTER VALIDATION
# ======================================================
def test_semester_valid(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
page.select_semester("5")
page.click_save()
page.wait_until_reload_after_save()
assert page.get_selected_semester() == "5"
def test_semester_dropdown_options(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
options = page.get_all_semester_options()
assert options == ["1", "2", "3", "4", "5", "6", "7", "8"]

View File

@ -0,0 +1,114 @@
import os
import pytest
from pages.profile_page import ProfilePage
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
TEST_DATA_DIR = os.path.join(BASE_DIR, "test_data")
class TestProfileUpload:
def test_upload_jpg_berhasil(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
"""
Upload file JPG berhasil dan preview berubah
"""
page.wait_until_page_loaded()
old_src = page.get_profile_image_src()
file_path = os.path.join(TEST_DATA_DIR, "Kucink_Jpg.jpg")
page.upload_photo(file_path)
page.click_save()
page.refresh_page()
new_src = page.get_profile_image_src()
assert old_src != new_src
def test_upload_png_berhasil(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
"""
Upload file PNG berhasil
"""
old_src = page.get_profile_image_src()
file_path = os.path.join(TEST_DATA_DIR, "Kucink_PNG.png")
page.upload_photo(file_path)
page.click_save()
page.refresh_page()
new_src = page.get_profile_image_src()
assert old_src != new_src
def test_upload_file_selain_gambar_ditolak(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
"""
Upload file selain JPG/PNG harus ditolak
"""
old_src = page.get_profile_image_src()
file_path = os.path.join(TEST_DATA_DIR, "invalid_file.pdf")
page.upload_photo(file_path)
page.click_save()
page.refresh_page()
new_src = page.get_profile_image_src()
assert old_src == new_src
def test_upload_file_lebih_800kb_ditolak(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
"""
Upload file > 800KB harus gagal
"""
old_src = page.get_profile_image_src()
file_path = os.path.join(TEST_DATA_DIR, "large_image.jpg")
page.upload_photo(file_path)
page.click_save()
page.refresh_page()
new_src = page.get_profile_image_src()
assert old_src == new_src
def test_preview_muncul_setelah_upload(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
"""
Preview image harus berubah sebelum save
"""
old_src = page.get_profile_image_src()
file_path = os.path.join(TEST_DATA_DIR, "Kucink_Jpg.jpg")
page.upload_photo(file_path)
new_src = page.get_profile_image_src()
assert old_src != new_src
def test_reset_mengembalikan_foto_default(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/profile")
page = ProfilePage(driver)
"""
Reset harus mengembalikan foto ke default
"""
page.wait_until_page_loaded()
page.click_reset_photo()
page.refresh_page()
src = page.get_profile_image_src()
assert "defaultProfile" in src

View File

@ -0,0 +1,75 @@
import pytest
from datetime import datetime
@pytest.mark.usefixtures("driver")
class TestRegisterAngkatan:
"""
RULE ANGKATAN:
- Wajib diisi (HTML5)
- Hanya angka
- Minimal 2021
- Maksimal tahun sekarang + 1
"""
# =========================
# NEGATIVE TEST CASES
# =========================
def test_angkatan_kosong(self, register_page, valid_register_data):
valid_register_data["angkatan"] = ""
register_page.fill_form(valid_register_data)
assert register_page.is_field_required("angkatan")
def test_angkatan_hanya_spasi(self, register_page, valid_register_data):
valid_register_data["angkatan"] = " "
register_page.fill_form(valid_register_data)
assert register_page.is_field_required("angkatan")
def test_angkatan_huruf(self, register_page, valid_register_data):
valid_register_data["angkatan"] = "abcd"
register_page.fill_form(valid_register_data)
value = register_page.get_value("angkatan")
assert value == ""
def test_angkatan_simbol(self, register_page, valid_register_data):
valid_register_data["angkatan"] = "&&&&"
register_page.fill_form(valid_register_data)
value = register_page.get_value("angkatan")
assert value == ""
def test_angkatan_kurang_dari_minimum(self, register_page, valid_register_data):
valid_register_data["angkatan"] = "2019"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_html5_validation("angkatan")
def test_angkatan_terlalu_besar(self, register_page, valid_register_data):
valid_register_data["angkatan"] = "9999"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_html5_validation("angkatan")
# =========================
# POSITIVE TEST CASES
# =========================
def test_angkatan_valid_minimum(self, register_page, valid_register_data):
valid_register_data["angkatan"] = "2022"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()
def test_angkatan_valid_tahun_sekarang(self, register_page, valid_register_data):
tahun_sekarang = str(datetime.now().year)
valid_register_data["angkatan"] = tahun_sekarang
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()
def test_angkatan_valid_random(self, register_page, valid_register_data):
valid_register_data["angkatan"] = valid_register_data["angkatan"]
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()

View File

@ -0,0 +1,147 @@
import pytest
@pytest.mark.usefixtures("driver")
class TestRegisterEmail:
"""
RULE EMAIL:
- Wajib diisi (HTML5)
- Harus format email valid (HTML5)
- Tidak boleh mengandung spasi
- Domain wajib:
@polije.ac.id
@student.polije.ac.id
- Validasi domain & email aktif menggunakan backend / JS
"""
# =========================
# NEGATIVE TEST CASES
# =========================
def test_email_kosong(self, register_page, valid_register_data):
valid_register_data["email"] = ""
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_field_required("email")
def test_email_hanya_spasi(self, register_page, valid_register_data):
valid_register_data["email"] = " "
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_field_required("email")
def test_email_tanpa_at(self, register_page, valid_register_data):
valid_register_data["email"] = "userpolije.ac.id"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_html5_validation("email")
def test_email_angka_semua(self, register_page, valid_register_data):
valid_register_data["email"] = "123456789"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_html5_validation("email")
def test_nama_email_angka_semua(self, register_page, valid_register_data):
valid_register_data["email"] = "12345678@student.polije.ac.id"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("Email")
def test_email_simbol_semua(self, register_page, valid_register_data):
valid_register_data["email"] = "&&&&"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_html5_validation("email")
def test_email_mengandung_spasi(self, register_page, valid_register_data):
valid_register_data["email"] = "user @polije.ac.id"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_html5_validation("email")
def test_email_domain_bukan_polije(self, register_page, valid_register_data):
valid_register_data["email"] = "user@gmail.com"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("Email")
def test_email_domain_salah(self, register_page, valid_register_data):
valid_register_data["email"] = "user@student.polije.co.id"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("Email")
def test_email_terdaftar_typo(self, register_page, valid_register_data):
valid_register_data["email"] = "e41222052@polije.acid"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("Email")
def test_email_tidak_aktif(self, register_page, valid_register_data):
valid_register_data["email"] = "email.tidak.aktif@student.polije.ac.id"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("Email")
def test_email_mengandung_dash(self, register_page, valid_register_data):
valid_register_data["email"] = "user-test@student.polije.ac.id"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("Email")
def test_email_mengandung_plus(self, register_page, valid_register_data):
valid_register_data["email"] = "user+test@student.polije.ac.id"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("Email")
def test_register_dua_kali_email_sama(self, register_page, valid_register_data):
email = valid_register_data["email"]
# Register pertama
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()
# Register kedua → email sama, nama & NIM beda
register_page.open()
data_kedua = valid_register_data.copy()
data_kedua["email"] = email
data_kedua["nama_lengkap"] = "User Kedua Unik"
data_kedua["nim"] = "E41229999"
register_page.fill_form(data_kedua)
register_page.submit()
assert register_page.has_error("Email")
# =========================
# POSITIVE TEST CASES
# =========================
def test_email_mengandung_underscore(self, register_page, valid_register_data):
valid_register_data["email"] = "user_test@student.polije.ac.id"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()
def test_email_valid_polije(self, register_page, valid_register_data):
valid_register_data["email"] = "dosen@polije.ac.id"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()
def test_email_valid_student_polije(self, register_page, valid_register_data):
valid_register_data["email"] = "E41222050@student.polije.ac.id"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()

View File

@ -0,0 +1,87 @@
import pytest
@pytest.mark.usefixtures("driver")
class TestRegisterKonfirmasiPassword:
"""
RULE KONFIRMASI PASSWORD:
- Wajib diisi
- Harus sama dengan password
- Validasi muncul setelah klik submit
"""
# =========================
# NEGATIVE TEST CASES
# =========================
def test_konfirmasi_password_kosong(self, register_page, valid_register_data):
"""
Konfirmasi password kosong HTML5 required
"""
valid_register_data["password"] = "Abcdef1@"
valid_register_data["konfirmasi_password"] = ""
register_page.fill_form(valid_register_data)
assert register_page.is_field_required("konfirmasi_password")
def test_konfirmasi_password_hanya_spasi(self, register_page, valid_register_data):
valid_register_data["password"] = "Abcdef1@"
valid_register_data["konfirmasi_password"] = " "
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("Password")
def test_konfirmasi_password_tidak_sama(self, register_page, valid_register_data):
valid_register_data["password"] = "Abcdef1@"
valid_register_data["konfirmasi_password"] = "Abcdef2@"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("Password")
def test_konfirmasi_password_lebih_pendek(self, register_page, valid_register_data):
valid_register_data["password"] = "Abcdef1@"
valid_register_data["konfirmasi_password"] = "Abcdef1"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("Password")
def test_konfirmasi_password_lebih_panjang(self, register_page, valid_register_data):
valid_register_data["password"] = "Abcdef1@"
valid_register_data["konfirmasi_password"] = "Abcdef1@xxx"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("Password")
# =========================
# POSITIVE TEST CASES
# =========================
def test_konfirmasi_password_sama(self, register_page, valid_register_data):
valid_register_data["password"] = "Abcdef1@"
valid_register_data["konfirmasi_password"] = "Abcdef1@"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()
def test_konfirmasi_password_valid_dengan_spasi(self, register_page, valid_register_data):
"""
Jika password mengandung spasi dan konfirmasi sama valid
"""
valid_register_data["password"] = "Abc def1@"
valid_register_data["konfirmasi_password"] = "Abc def1@"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()

View File

@ -0,0 +1,100 @@
import pytest
from pages.register_page import RegisterPage
@pytest.mark.usefixtures("driver")
class TestRegisterNamaLengkap:
"""
RULE FINAL NAMA LENGKAP:
- Minimal 5 karakter
- Maksimal 30 karakter
- HANYA boleh huruf dan spasi
- Huruf kapital/kecil bebas
- TIDAK BOLEH angka atau simbol
"""
# =========================
# NEGATIVE TEST CASES
# =========================
def test_nama_kosong(self, register_page, valid_register_data):
valid_register_data["nama_lengkap"] = ""
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_field_required("nama_lengkap")
def test_nama_hanya_spasi(self, register_page, valid_register_data):
valid_register_data["nama_lengkap"] = " "
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("Nama Lengkap")
def test_nama_kurang_dari_5_karakter(self, register_page, valid_register_data):
valid_register_data["nama_lengkap"] = "Aa"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("Nama Lengkap")
def test_nama_lebih_dari_30_karakter(self, register_page, valid_register_data):
valid_register_data["nama_lengkap"] = "B" * 31
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("Nama Lengkap")
def test_nama_angka_semua(self, register_page, valid_register_data):
valid_register_data["nama_lengkap"] = "98076545"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("Nama Lengkap")
def test_nama_simbol_semua(self, register_page, valid_register_data):
valid_register_data["nama_lengkap"] = "######"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("Nama Lengkap")
def test_nama_huruf_dan_angka(self, register_page, valid_register_data):
valid_register_data["nama_lengkap"] = "Bagus123"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("Nama Lengkap")
def test_nama_huruf_dan_simbol(self, register_page, valid_register_data):
valid_register_data["nama_lengkap"] = "Gilang@Putra"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("Nama Lengkap")
# =========================
# POSITIVE TEST CASES
# =========================
def test_nama_valid_huruf_kecil(self, register_page, valid_register_data):
valid_register_data["nama_lengkap"] = "gilang bagus"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()
def test_nama_valid_huruf_kapital(self, register_page, valid_register_data):
valid_register_data["nama_lengkap"] = "GILANG RAMADAN"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()
def test_nama_valid_huruf_dan_spasi(self, register_page, valid_register_data):
valid_register_data["nama_lengkap"] = "Gilang Rama"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()
def test_nama_valid_minimal_karakter(self, register_page, valid_register_data):
valid_register_data["nama_lengkap"] = "Rurin Nur"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()
def test_nama_valid_maksimal_karakter(self, register_page, valid_register_data):
valid_register_data["nama_lengkap"] = "C" * 30
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()

View File

@ -0,0 +1,96 @@
import pytest
@pytest.mark.usefixtures("driver")
class TestRegisterNIM:
"""
RULE FINAL NIM:
- Tidak boleh kosong (HTML5 required)
- Tidak boleh spasi
- Harus kombinasi huruf + angka
- Tidak boleh karakter khusus
- Minimal 9 karakter
- Maksimal 10 karakter
"""
# =========================
# REQUIRED (HTML5)
# =========================
def test_nim_kosong(self, register_page, valid_register_data):
valid_register_data["nim"] = ""
register_page.fill_form(valid_register_data)
# ❌ JANGAN submit
assert register_page.is_field_required("NIM")
# =========================
# NEGATIVE (SERVER / JS)
# =========================
def test_nim_spasi_saja(self, register_page, valid_register_data):
valid_register_data["nim"] = " "
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("NIM")
def test_nim_mengandung_spasi(self, register_page, valid_register_data):
valid_register_data["nim"] = "E 4122987"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("NIM")
def test_nim_hanya_angka(self, register_page, valid_register_data):
valid_register_data["nim"] = "412227890"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("NIM")
def test_nim_hanya_huruf(self, register_page, valid_register_data):
valid_register_data["nim"] = "ABCDEFGHI"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("NIM")
def test_nim_karakter_khusus(self, register_page, valid_register_data):
valid_register_data["nim"] = "E41222@89"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("NIM")
def test_nim_kurang_dari_9_karakter(self, register_page, valid_register_data):
valid_register_data["nim"] = "E41222"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("NIM")
def test_nim_lebih_dari_10_karakter(self, register_page, valid_register_data):
valid_register_data["nim"] = "E41222789099"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("NIM")
# =========================
# POSITIVE
# =========================
def test_nim_valid_9_karakter(self, register_page, valid_register_data):
valid_register_data["nim"] = "E41222050"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()
def test_nim_valid_10_karakter(self, register_page, valid_register_data):
valid_register_data["nim"] = "E412227890"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()

View File

@ -0,0 +1,160 @@
import pytest
@pytest.mark.usefixtures("driver")
class TestRegisterPassword:
"""
RULE PASSWORD:
- Wajib diisi (HTML5)
- Minimal 8 karakter
- Maksimal 12 karakter
- Boleh mengandung spasi
- Wajib mengandung:
- Huruf besar
- Huruf kecil
- Angka atau karakter khusus
- Validasi muncul setelah klik submit
"""
# =========================
# HTML5 VALIDATION
# =========================
def test_password_kosong(self, register_page, valid_register_data):
"""
Password kosong HTML5 required
"""
valid_register_data["password"] = ""
valid_register_data["konfirmasi_password"] = ""
register_page.fill_form(valid_register_data)
# TIDAK perlu submit, HTML5 sudah aktif
assert register_page.is_field_required("password")
# =========================
# NEGATIVE TEST CASES (CUSTOM VALIDATION)
# =========================
def test_password_kurang_dari_minimum(self, register_page, valid_register_data):
valid_register_data["password"] = "Ab1@"
valid_register_data["konfirmasi_password"] = "Ab1@"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("Password")
def test_password_lebih_dari_maksimum(self, register_page, valid_register_data):
valid_register_data["password"] = "Abcdef1@xxxx"
valid_register_data["konfirmasi_password"] = "Abcdef1@xxxx"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("Password")
def test_password_tanpa_huruf_kapital(self, register_page, valid_register_data):
valid_register_data["password"] = "abcdef1@"
valid_register_data["konfirmasi_password"] = "abcdef1@"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("Password")
def test_password_tanpa_huruf_kecil(self, register_page, valid_register_data):
valid_register_data["password"] = "ABCDEF1@"
valid_register_data["konfirmasi_password"] = "ABCDEF1@"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("Password")
def test_password_tanpa_angka(self, register_page, valid_register_data):
valid_register_data["password"] = "Abcdef@@"
valid_register_data["konfirmasi_password"] = "Abcdef@@"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("Password")
def test_password_tanpa_karakter_khusus(self, register_page, valid_register_data):
valid_register_data["password"] = "Abcdef12"
valid_register_data["konfirmasi_password"] = "Abcdef12"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("Password")
@pytest.mark.parametrize(
"password",
[
"12345678", # angka semua
"abcdefgh", # huruf kecil semua
"ABCDEFGH", # huruf besar semua
"&&&&&&&&", # simbol semua
]
)
def test_password_komposisi_tidak_valid(
self, register_page, valid_register_data, password
):
valid_register_data["password"] = password
valid_register_data["konfirmasi_password"] = password
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("Password")
def test_password_mengandung_spasi(self, register_page, valid_register_data):
"""
Spasi BOLEH, selama rule lain terpenuhi
"""
valid_register_data["password"] = "Abc def1@"
valid_register_data["konfirmasi_password"] = "Abc def1@"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()
def test_password_tepat_minimum(self, register_page, valid_register_data):
valid_register_data["password"] = "Abcd1@xy"
valid_register_data["konfirmasi_password"] = "Abcd1@xy"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()
def test_password_tepat_maksimum(self, register_page, valid_register_data):
"""
Boundary value: tepat 12 karakter
"""
valid_register_data["password"] = "Abcd12@xyzQ"
valid_register_data["konfirmasi_password"] = "Abcd12@xyzQ"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()
def test_password_valid_kriteria(self, register_page, valid_register_data):
"""
Boundary value: tepat 8 karakter
"""
valid_register_data["password"] = "#Qwerty123"
valid_register_data["konfirmasi_password"] = "#Qwerty123"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()
# =========================
# POSITIVE TEST CASE
# =========================

View File

@ -0,0 +1,73 @@
import pytest
@pytest.mark.usefixtures("driver")
class TestRegisterSemester:
"""
RULE SEMESTER:
- Wajib diisi (HTML5)
- Hanya angka
- Range 1 - 14
"""
# =========================
# NEGATIVE TEST CASES
# =========================
def test_semester_kosong(self, register_page, valid_register_data):
valid_register_data["semester"] = ""
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_field_required("semester")
def test_semester_hanya_spasi(self, register_page, valid_register_data):
valid_register_data["semester"] = " "
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_field_required("semester")
def test_semester_huruf(self, register_page, valid_register_data):
valid_register_data["semester"] = "abc"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("semester")
def test_semester_simbol(self, register_page, valid_register_data):
valid_register_data["semester"] = "&&"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("semester")
def test_semester_kurang_dari_minimum(self, register_page, valid_register_data):
valid_register_data["semester"] = "0"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("semester")
def test_semester_lebih_dari_maksimum(self, register_page, valid_register_data):
valid_register_data["semester"] = "15"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.has_error("semester")
# =========================
# POSITIVE TEST CASES
# =========================
def test_semester_valid_minimum(self, register_page, valid_register_data):
valid_register_data["semester"] = "1"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()
def test_semester_valid_tengah(self, register_page, valid_register_data):
valid_register_data["semester"] = "8"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()
def test_semester_valid_maksimum(self, register_page, valid_register_data):
valid_register_data["semester"] = "14"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()

View File

@ -0,0 +1,163 @@
import pytest
import os
from pages.register_page import RegisterPage
from pages.live_coding_page import LiveCodingPage
from pages.profile_page import ProfilePage
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
TEST_DATA_DIR = os.path.join(BASE_DIR, "test_data")
@pytest.mark.usefixtures("driver")
class TestRegression:
#Regression Daftar Akun
def test_nama_valid_huruf_kecil(self, register_page, valid_register_data):
valid_register_data["nama_lengkap"] = "rurinhaliza"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()
def test_nim_valid_9_karakter(self, register_page, valid_register_data):
valid_register_data["nim"] = "E41222555"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()
def test_semester_valid_tengah(self, register_page, valid_register_data):
valid_register_data["semester"] = "4"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()
def test_angkatan_valid_random(self, register_page, valid_register_data):
valid_register_data["angkatan"] = valid_register_data["angkatan"]
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()
def test_email_valid_student_polije(self, register_page, valid_register_data):
valid_register_data["email"] = "siswa@student.polije.ac.id"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()
def test_password_valid(self, register_page, valid_register_data):
"""
Password valid (huruf besar, kecil, angka, simbol)
"""
valid_register_data["password"] = "Qwerty098"
valid_register_data["konfirmasi_password"] = "Qwerty098"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()
def test_konfirmasi_password_sama(self, register_page, valid_register_data):
valid_register_data["password"] = "Zxcvbn123"
valid_register_data["konfirmasi_password"] = "Zxcvbn123"
register_page.fill_form(valid_register_data)
register_page.submit()
assert register_page.is_register_success()
# Regression materi kinesthetic
def test_submit_with_correct_code(self,driver,login_as_user_belum_kuesionerNew):
driver.get("https://hypermedialearning.project2025.id/public/materi/kinesthetic/1")
page = LiveCodingPage(driver)
page.append_code2("""
Mahasiswa
println
""")
page.submit_code()
result = page.get_result_message()
assert "Jawaban Benar" in result
print(result)
def test_next_button_on_first_page(self,driver,login_as_user_belum_kuesionerNew):
driver.get("https://hypermedialearning.project2025.id/public/materi/kinesthetic/1")
page = LiveCodingPage(driver)
initial_url = page.get_current_url()
page.click_next()
current_url = page.get_current_url()
assert "/kinesthetic/2" in current_url
#Regression Pofile
def test_email_tampil_sesuai_akun(self,driver,login_as_user_belum_kuesionerNew):
driver.get("https://hypermedialearning.project2025.id/public/profile")
page = ProfilePage(driver)
"""
Verifikasi email tampil sesuai akun dan tidak kosong
"""
page.wait_until_page_loaded()
email = page.get_email()
assert email is not None
#assert email != ""
assert "@" in email
assert email.endswith(".ac.id")
#or email.endswith(".com")
def test_upload_file_selain_gambar_ditolak(self,driver,login_as_user_belum_kuesionerNew):
driver.get("https://hypermedialearning.project2025.id/public/profile")
page = ProfilePage(driver)
"""
Upload file selain JPG/PNG harus ditolak
"""
old_src = page.get_profile_image_src()
file_path = os.path.join(TEST_DATA_DIR, "invalid_file.pdf")
page.upload_photo(file_path)
page.click_save()
page.refresh_page()
new_src = page.get_profile_image_src()
assert old_src == new_src
def test_nama_kosong(self,driver,login_as_user_belum_kuesionerNew):
driver.get("https://hypermedialearning.project2025.id/public/profile")
page = ProfilePage(driver)
default_name = page.get_nama_lengkap()
page.set_nama_lengkap("")
page.click_save()
assert page.get_nama_lengkap() == default_name
def test_nim_kosong(self,driver,login_as_user_belum_kuesionerNew):
driver.get("https://hypermedialearning.project2025.id/public/profile")
page = ProfilePage(driver)
default_nim = page.get_nim()
page.set_nim("")
page.click_save()
assert page.get_nim() == default_nim
def test_semester_valid(self,driver,login_as_user_belum_kuesionerNew):
driver.get("https://hypermedialearning.project2025.id/public/profile")
page = ProfilePage(driver)
page.select_semester("1")
page.click_save()
page.wait_until_reload_after_save()
assert page.get_selected_semester() == "1"
def test_angkatan_valid(self,driver,login_as_user_belum_kuesionerNew):
driver.get("https://hypermedialearning.project2025.id/public/profile")
page = ProfilePage(driver)
page.select_angkatan("2025")
page.click_save()
page.wait_until_reload_after_save()
assert page.get_selected_angkatan() == "2025"
def test_button_simpan_update_data(self,driver,login_as_user_belum_kuesionerNew):
driver.get("https://hypermedialearning.project2025.id/public/profile")
page = ProfilePage(driver)
new_name = "ItsTesting"
page.set_nama_lengkap(new_name)
page.click_save()
page.wait_until_reload_after_save()
assert page.get_nama_lengkap() == new_name

View File

@ -0,0 +1,50 @@
import pytest
from pages.resume_pembelajaran_page import ResumePembelajaranPage
from pages.materi_readwrite_page import MateriReadWritePage
class TestResumePembelajaran:
def test_resume_empty_state_displayed(self,driver,login_as_user_belum_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/resume-pembelajaran")
page = ResumePembelajaranPage(driver)
page.page_loaded()
assert page.is_empty_message_displayed() is True
def test_resume_created_after_submit_rangkuman(self,driver, login_as_user_sudah_kuesioner):
# 1. Kirim rangkuman dari Materi Read/Write
driver.get("https://hypermedialearning.sanggadewa.my.id/materi/readwrite/1")
materi_page = MateriReadWritePage(driver)
materi_page.page_loaded()
rangkuman_text = " ".join(["test resume"] * 50)
materi_page.input_rangkuman(rangkuman_text)
materi_page.submit_rangkuman()
# 2. Buka Resume Pembelajaran
driver.get(
"https://hypermedialearning.sanggadewa.my.id/resume-pembelajaran"
)
resume_page = ResumePembelajaranPage(driver)
resume_page.page_loaded()
resume_content = resume_page.get_resume_text()
assert rangkuman_text in resume_content
def test_resume_persist_after_page_reload(self,driver, login_as_user_sudah_kuesioner):
driver.get("https://hypermedialearning.sanggadewa.my.id/resume-pembelajaran")
page = ResumePembelajaranPage(driver)
page.page_loaded()
first_load_text = page.get_resume_text()
driver.refresh()
page.page_loaded()
second_load_text = page.get_resume_text()
assert first_load_text == second_load_text

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

View File

@ -0,0 +1,4 @@
INI FILE
UNTUK
TESTING

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 976 KiB

146
tests/test_register.py Normal file
View File

@ -0,0 +1,146 @@
from pages.register_page import RegisterPage
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
def test_register_valid_data(driver):
"""
Test Scenario:
Periksa sistem jika user mengisi seluruh form pendaftaran dengan data valid
Technical Requirement:
T1T6, T7T12, T13T20, T21T28, T29T41
Expected Result:
Sistem menerima pendaftaran akun
"""
register = RegisterPage(driver)
register.open()
# === DATA UNIK ===
timestamp = int(time.time())
email_unik = f"dummy_{timestamp}@polije.ac.id"
nim_unik = f"E41{timestamp % 100000}"
register.input_nama_lengkap("Budi Santoso")
register.input_nim("E41234567")
register.input_semester("6")
register.input_angkatan("2022")
register.input_email("dummy_test@polije.ac.id")
register.input_password("Password1!")
register.input_konfirmasi_password("Password1!")
register.submit()
# === WAIT PROSES SUBMIT ===
WebDriverWait(driver, 10).until(
lambda d: d.current_url != RegisterPage.URL
or len(register.get_error_message()) > 0
)
# === ASSERTION ===
assert "register" not in driver.current_url, \
"Registrasi gagal, user masih berada di halaman register"
def test_register_nama_lengkap_kosong(driver):
"""
Test Scenario:
Nama Lengkap tidak diisi
Technical Requirement:
T1 Nama Lengkap tidak boleh kosong
Expected Result:
Sistem menolak pendaftaran dan menampilkan validasi wajib isi
"""
register = RegisterPage(driver)
register.open()
# Nama kosong
register.input_nim("E41234567")
register.input_semester("6")
register.input_angkatan("2022")
register.input_email("test@polije.ac.id")
register.input_password("Password1!")
register.input_konfirmasi_password("Password1!")
register.submit()
assert register.is_field_invalid(RegisterPage.NAMA_LENGKAP)
assert "fill out" in register.get_validation_message(RegisterPage.NAMA_LENGKAP).lower()
def test_register_email_tidak_valid(driver):
register = RegisterPage(driver)
register.open()
register.input_nama_lengkap("Budi Santoso")
register.input_nim("E41234568")
register.input_semester("6")
register.input_angkatan("2022")
register.input_email("email-salah-format")
register.input_password("Password1!")
register.input_konfirmasi_password("Password1!")
register.submit()
assert register.is_field_invalid(RegisterPage.EMAIL)
def test_register_password_tidak_sama(driver):
register = RegisterPage(driver)
register.open()
register.input_nama_lengkap("Budi Santoso")
register.input_nim("E41234569")
register.input_semester("6")
register.input_angkatan("2022")
register.input_email("passwordbeda@polije.ac.id")
register.input_password("Password1!")
register.input_konfirmasi_password("Password2!")
register.submit()
errors = register.get_error_message()
assert len(errors) > 0
def test_register_nim_kosong(driver):
register = RegisterPage(driver)
register.open()
register.input_nama_lengkap("Budi Santoso")
register.input_semester("6")
register.input_angkatan("2022")
register.input_email("nimkosong@polije.ac.id")
register.input_password("Password1!")
register.input_konfirmasi_password("Password1!")
register.submit()
assert register.is_field_invalid(RegisterPage.NIM)
def test_register_semester_bukan_angka(driver):
register = RegisterPage(driver)
register.open()
register.input_nama_lengkap("Budi Santoso")
register.input_nim("E41234570")
register.input_semester("enam")
register.input_angkatan("2022")
register.input_email("semester@polije.ac.id")
register.input_password("Password1!")
register.input_konfirmasi_password("Password1!")
register.submit()
assert register.is_field_invalid(RegisterPage.SEMESTER)
def test_register_semua_field_kosong(driver):
register = RegisterPage(driver)
register.open()
# ASSERTION TANPA CLICK
assert register.is_field_invalid(RegisterPage.NAMA_LENGKAP)

61
utils/data_generator.py Normal file
View File

@ -0,0 +1,61 @@
import random
import string
import time
def random_string(length=6):
return ''.join(random.choices(string.ascii_letters, k=length))
def random_number(length=10):
return ''.join(random.choices(string.digits, k=length))
def valid_nama_lengkap():
return f"User {random_string(5)}"
def valid_nim():
return random_number(9)
def valid_semester():
return str(random.randint(1, 8))
def valid_angkatan():
return str(random.randint(2022, 2025))
def valid_email():
timestamp = int(time.time() * 1000)
return f"user{timestamp}@student.polije.ac.id"
def valid_password():
return "Test@1234"
def generate_valid_register_data():
"""
Data VALID & lengkap untuk form register
"""
return {
"nama": valid_nama_lengkap(),
"nim": valid_nim(),
"semester": valid_semester(),
"angkatan": valid_angkatan(),
"email": valid_email(),
"password": valid_password(),
}
def generate_valid_nim():
"""
NIM valid:
- huruf + angka
- panjang 910
Contoh: E41222789
"""
prefix = random.choice(string.ascii_uppercase)
digits = ''.join(random.choices(string.digits, k=random.randint(8, 9)))
return prefix + digits