diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index 8897a8a..3458be9 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -124,6 +124,7 @@ jobs: jupyterhub_enabled: false flowable_enabled: false wiki_enabled: false + member_display_enabled: true - name: user-metadata display_name: "User Tests (Metadata)" include_admin: false @@ -139,6 +140,7 @@ jobs: jupyterhub_enabled: false flowable_enabled: false wiki_enabled: false + member_display_enabled: false - name: admin display_name: "Admin Tests" include_admin: true @@ -152,6 +154,7 @@ jobs: jupyterhub_enabled: false flowable_enabled: false wiki_enabled: false + member_display_enabled: false exclude_notebooks: | テスト手順-管理者機能-ログイン可否設定.ipynb テスト手順-管理者機能-ノード管理.ipynb @@ -195,6 +198,7 @@ jobs: jupyterhub_enabled: false flowable_enabled: false wiki_enabled: false + member_display_enabled: false - name: migration-from-20250906-weko display_name: "Migration Test from 20250906 (WEKO)" include_admin: true @@ -222,6 +226,7 @@ jobs: jupyterhub_enabled: false flowable_enabled: false wiki_enabled: false + member_display_enabled: false - name: user-minio display_name: "User Tests (MinIO)" include_admin: false @@ -238,6 +243,7 @@ jobs: jupyterhub_enabled: false flowable_enabled: false wiki_enabled: false + member_display_enabled: false - name: admin-minio display_name: "Admin Tests (MinIO)" include_admin: true @@ -253,6 +259,7 @@ jobs: jupyterhub_enabled: false flowable_enabled: false wiki_enabled: false + member_display_enabled: false - name: user-weko-direct display_name: "User Tests (WEKO Direct)" include_admin: true @@ -268,6 +275,7 @@ jobs: jupyterhub_enabled: false flowable_enabled: false wiki_enabled: false + member_display_enabled: false - name: user-weko-workflow display_name: "User Tests (WEKO Workflow)" include_admin: true @@ -283,6 +291,7 @@ jobs: jupyterhub_enabled: false flowable_enabled: false wiki_enabled: false + member_display_enabled: false - name: user-jupyterhub display_name: "User Tests (JupyterHub)" include_admin: false @@ -297,6 +306,7 @@ jobs: jupyterhub_enabled: true flowable_enabled: false wiki_enabled: false + member_display_enabled: false - name: user-workflow-roles display_name: "User Tests (Flowable Roles)" include_admin: true @@ -312,6 +322,7 @@ jobs: flowable_enabled: true workflow_test_mode: roles wiki_enabled: false + member_display_enabled: false - name: user-workflow-forms display_name: "User Tests (Flowable Forms)" include_admin: true @@ -327,6 +338,7 @@ jobs: flowable_enabled: true workflow_test_mode: forms wiki_enabled: false + member_display_enabled: false - name: user-workflow-batch display_name: "User Tests (Flowable Batch)" include_admin: true @@ -343,6 +355,7 @@ jobs: workflow_test_mode: batch workflow_batch_project_count: 50 wiki_enabled: false + member_display_enabled: false - name: user-wiki display_name: "User Tests (Wiki)" include_admin: false @@ -357,6 +370,7 @@ jobs: jupyterhub_enabled: false flowable_enabled: false wiki_enabled: true + member_display_enabled: false - name: user-metadata-mibyo-db display_name: "User Tests (Mibyo DB)" include_admin: true @@ -372,6 +386,7 @@ jobs: jupyterhub_enabled: false flowable_enabled: false wiki_enabled: false + member_display_enabled: false name: E2E ${{ matrix.test-group.display_name }} env: MINIO_ENABLED: ${{ matrix.test-group.minio_enabled == true && 'true' || 'false' }} @@ -1322,10 +1337,13 @@ jobs: idp_name_1: 'FakeCAS' idp_username_1: 'testuser1@example.com' idp_password_1: 'testpass123' + # fullname of testuser1/testuser2 in .github/scripts/setup_test_data.py (shown in contributor list) + idp_user_display_name_1: 'Test User 1' idp_name_2: 'FakeCAS' idp_username_2: 'testuser2@example.com' idp_password_2: 'testpass456' + idp_user_display_name_2: 'Test User 2' # Admin users for admin page tests idp_name_integrated_admin: 'FakeCAS' @@ -1368,6 +1386,7 @@ jobs: weko_enabled: ${{ matrix.test-group.weko_enabled == true }} weko_test_mode: ${{ matrix.test-group.weko_test_mode || 'direct' }} mibyo_db_enabled: ${{ matrix.test-group.mibyo_db_enabled == true }} + member_display_enabled: ${{ matrix.test-group.member_display_enabled == true }} enable_1gb_file_upload: false skip_autofill: ${{ matrix.test-group.skip_autofill == true }} storages_oauth: [] diff --git a/run_tests.py b/run_tests.py index d27ea28..d24509c 100644 --- a/run_tests.py +++ b/run_tests.py @@ -74,6 +74,8 @@ def __init__(self, config_path, show_disk_usage=False, failed_result_path=None): self.gateway_base_url = None # Wiki specific parameters self.wiki_enabled = False + # Member display specific parameters + self.member_display_enabled = False # Workflow admin user parameters self.idp_name_integrated_admin = None self.idp_username_integrated_admin = None @@ -629,6 +631,24 @@ def run_wiki_tests(self): exclude_notebooks=self.exclude_notebooks, ) ) + + def run_member_display_tests(self): + """Run member display (contributors screen) tests.""" + print('\n=== Member Display Tests ===') + if not self.member_display_enabled: + print('Skipping Member Display tests (member_display_enabled=false)') + return + + self.result_notebooks.append( + self.run_notebook( + 'テスト手順-画面表示-メンバー表示機能.ipynb', + idp_user_display_name_1=getattr(self, 'idp_user_display_name_1', None), + idp_name_2=getattr(self, 'idp_name_2', None), + idp_username_2=getattr(self, 'idp_username_2', None), + idp_user_display_name_2=getattr(self, 'idp_user_display_name_2', None), + idp_password_2=getattr(self, 'idp_password_2', None), + ) + ) def check_notebook_errors(self, notebook_path): @@ -736,6 +756,7 @@ def run_all_tests(self): self.run_mibyo_db_tests() self.run_workflow_tests() self.run_wiki_tests() + self.run_member_display_tests() result_notebooks = [result_notebook for result_notebook in self.result_notebooks if result_notebook is not None] diff --git "a/\343\203\206\343\202\271\343\203\210\346\211\213\351\240\206-\347\224\273\351\235\242\350\241\250\347\244\272-\343\203\241\343\203\263\343\203\220\343\203\274\350\241\250\347\244\272\346\251\237\350\203\275.ipynb" "b/\343\203\206\343\202\271\343\203\210\346\211\213\351\240\206-\347\224\273\351\235\242\350\241\250\347\244\272-\343\203\241\343\203\263\343\203\220\343\203\274\350\241\250\347\244\272\346\251\237\350\203\275.ipynb" new file mode 100644 index 0000000..5a8e15c --- /dev/null +++ "b/\343\203\206\343\202\271\343\203\210\346\211\213\351\240\206-\347\224\273\351\235\242\350\241\250\347\244\272-\343\203\241\343\203\263\343\203\220\343\203\274\350\241\250\347\244\272\346\251\237\350\203\275.ipynb" @@ -0,0 +1,633 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "parameters", + "metadata": { + "tags": [ + "parameters" + ] + }, + "outputs": [], + "source": [ + "from datetime import datetime\n", + "from getpass import getpass\n", + "\n", + "rdm_url = 'https://rdm.example.com/'\n", + "idp_name_1 = None # 'GakuNin RDM IdP'\n", + "idp_username_1 = None\n", + "idp_user_display_name_1 = None\n", + "idp_password_1 = None\n", + "idp_name_2 = None # 'GakuNin RDM IdP'\n", + "idp_username_2 = None\n", + "idp_user_display_name_2 = None\n", + "idp_password_2 = None\n", + "project_name = None\n", + "delete_project = True\n", + "default_result_path = None\n", + "close_on_fail = False\n", + "transition_timeout = 30000" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "input-handling", + "metadata": {}, + "outputs": [], + "source": [ + "import tempfile\n", + "\n", + "if idp_username_1 is None:\n", + " idp_username_1 = input(prompt=f'Username for Existing User 1 ({idp_name_1})')\n", + "if idp_user_display_name_1 is None:\n", + " idp_user_display_name_1 = input(prompt=f'Display name for Existing User 1 ({idp_name_1})')\n", + "if idp_password_1 is None:\n", + " idp_password_1 = getpass(prompt=f'Password for {idp_username_1}@{idp_name_1}')\n", + "\n", + "if idp_username_2 is None:\n", + " idp_username_2 = input(prompt=f'Username for Existing User 2 ({idp_name_2})')\n", + "if idp_user_display_name_2 is None:\n", + " idp_user_display_name_2 = input(prompt=f'Display name for Existing User 2 ({idp_name_2})')\n", + "if idp_password_2 is None:\n", + " idp_password_2 = getpass(prompt=f'Password for {idp_username_2}@{idp_name_2}')\n", + "\n", + "if project_name is None:\n", + " project_name = datetime.now().strftime('TEST-ScreenDisplay-%Y%m%d-Contributor')\n" + ] + }, + { + "cell_type": "markdown", + "id": "header", + "metadata": {}, + "source": [ + "# メンバー画面の表示項目の確認\n", + "\n", + "- サブシステム名: 画面表示\n", + "- ページ/アドオン: Member\n", + "- 機能分類: 画面表示\n", + "- シナリオ名: メンバー画面の表示項目確認(プロジェクト作成者/招待されたメンバー双方の視点)\n", + "- 用意するテストデータ: URL一覧、アカウント(既存ユーザー1, 既存ユーザー2)\n", + "- 事前条件: 既存ユーザー1、既存ユーザー2のアカウントが利用可能であること" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "work-dir", + "metadata": {}, + "outputs": [], + "source": [ + "work_dir = tempfile.mkdtemp()\n", + "if default_result_path is None:\n", + " default_result_path = work_dir\n", + "work_dir" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "init-context", + "metadata": {}, + "outputs": [], + "source": [ + "import importlib\n", + "import pandas as pd\n", + "\n", + "import scripts.playwright\n", + "importlib.reload(scripts.playwright)\n", + "\n", + "from scripts.playwright import *\n", + "from scripts import grdm\n", + "\n", + "await init_pw_context(close_on_fail=close_on_fail, last_path=default_result_path)" + ] + }, + { + "cell_type": "markdown", + "id": "step1-header", + "metadata": {}, + "source": [ + "## ウェブブラウザの新規プライベートウィンドウでGRDMトップページを表示する\n", + "\n", + "GRDMトップページが表示されること" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "step1", + "metadata": {}, + "outputs": [], + "source": [ + "async def _step(page):\n", + " await page.goto(rdm_url)\n", + "\n", + " # 同意する をクリック\n", + " await page.locator('//button[text() = \"同意する\"]').click()\n", + "\n", + " # 同意する が表示されなくなったことを確認\n", + " await expect(page.locator('//button[text() = \"同意する\"]')).to_have_count(0, timeout=500)\n", + "\n", + "await run_pw(_step, new_context=True, new_page=True)" + ] + }, + { + "cell_type": "markdown", + "id": "step2-header", + "metadata": {}, + "source": [ + "## 「GakuNinRDM IdP」を利用し、既存ユーザー1としてログインする\n", + "\n", + "GRDMダッシュボードが表示されること" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "step2", + "metadata": {}, + "outputs": [], + "source": [ + "async def _step(page):\n", + " await grdm.login(page, idp_name_1, idp_username_1, idp_password_1, transition_timeout=transition_timeout)\n", + " await grdm.expect_dashboard(page, transition_timeout=transition_timeout)\n", + "\n", + "await run_pw(_step)" + ] + }, + { + "cell_type": "markdown", + "id": "step3-header", + "metadata": {}, + "source": [ + "## プロジェクト一覧に指定されたタイトルのプロジェクトがない場合、指定された名前でプロジェクトを作成する。 ダッシュボードから「新規プロジェクト作成」をクリックする。 タイトル「TEST-ScreenDisplay-YYYYMMDD(本日の日付)-Contributor」でプロジェクトを作成する\n", + "\n", + "\n", + "- 「新規プロジェクトの作成」のダイアログが表示されること\n", + "- ダッシュボードのプロジェクト一覧に入力したプロジェクト名のプロジェクトが追加されること" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "step3", + "metadata": {}, + "outputs": [], + "source": [ + "async def _step(page):\n", + " await grdm.ensure_project_exists(page, project_name, transition_timeout=transition_timeout)\n", + "\n", + "await run_pw(_step)" + ] + }, + { + "cell_type": "markdown", + "id": "step4-header", + "metadata": {}, + "source": [ + "## ダッシュボードのプロジェクト一覧から作成したプロジェクトをクリックする\n", + "\n", + "作成したプロジェクトのプロジェクトダッシュボードが表示されること" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "step4", + "metadata": {}, + "outputs": [], + "source": [ + "project_url = None\n", + "\n", + "async def _step(page):\n", + " global project_url\n", + " await page.locator(f'//*[@data-test-dashboard-item-title and text() = \"{project_name}\"]').click()\n", + " await expect(page.locator('//span[@id = \"nodeTitleEditable\"]')).to_be_visible(timeout=transition_timeout)\n", + " project_url = page.url\n", + "\n", + "await run_pw(_step)" + ] + }, + { + "cell_type": "markdown", + "id": "step5-header", + "metadata": {}, + "source": [ + "## プロジェクトダッシュボードの上部メニューから「メンバー」をクリックする\n", + "\n", + "- 「メンバー」画面が表示されること\n", + "- 「メンバー」にプロジェクト作成者自身が「管理者」権限で表示されること\n", + "- 「メンバー」に「名前」「E-mail」「所属機関」「招待日」「権限」「目録表示」の項目が表示されること" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "step5", + "metadata": {}, + "outputs": [], + "source": [ + "async def _step(page):\n", + " await page.locator('#projectSubnav').get_by_role('link', name='メンバー').click()\n", + " await expect(page.locator('#manageContributors')).to_be_visible(timeout=transition_timeout)\n", + "\n", + " creator_row = page.locator('#manageContributorsTable').locator(\n", + " f'//tr[.//*[contains(@class, \"name-search\") and normalize-space(text())=\"{idp_user_display_name_1}\"]]'\n", + " )\n", + " await expect(creator_row.locator('td.permissions select')).to_have_value('admin', timeout=transition_timeout)\n", + "\n", + " for header_text in ['名前', '権限', '目録表示', 'E-mail', '所属機関', '招待日']:\n", + " await expect(page.locator('#manageContributorsTable').get_by_text(header_text, exact=False).first).to_be_visible(timeout=transition_timeout)\n", + "\n", + "await run_pw(_step)" + ] + }, + { + "cell_type": "markdown", + "id": "step6-header", + "metadata": {}, + "source": [ + "## 「メンバー」のタイトルの横にある「+追加」をクリックする\n", + "\n", + "「メンバーを追加」ダイアログが表示されること" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "step6", + "metadata": {}, + "outputs": [], + "source": [ + "async def _step(page):\n", + " await page.locator('a[href=\"#addContributors\"]').click()\n", + " await expect(page.locator('#addContributors')).to_be_visible(timeout=transition_timeout)\n", + " await expect(page.locator('#addContributors .modal-title')).to_have_text('メンバーを追加', timeout=transition_timeout)\n", + "\n", + "await run_pw(_step)" + ] + }, + { + "cell_type": "markdown", + "id": "step7-header", + "metadata": {}, + "source": [ + "## 既存ユーザー2の情報(GUID,メールアドレス,名前)を入力して、「検索」ボタンをクリックする\n", + "\n", + "「結果」の一覧に既存ユーザー2が表示されること" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "step7", + "metadata": {}, + "outputs": [], + "source": [ + "async def _step(page):\n", + " search_box = page.locator('#addContributors .input-group input.form-control')\n", + " # テスト仕様書の「GUID, メールアドレス, 名前」のうち、名前(idp_user_display_name_2)で検索する\n", + " await search_box.fill(idp_user_display_name_2)\n", + " await page.locator('#addContributors input[type=\"submit\"][value=\"検索\"]').click()\n", + "\n", + " # 検索結果一覧は contributor.fullname で表示される(ログインユーザー名とは異なる場合がある)\n", + " result_row = page.locator('#addContributors').locator(\n", + " f'//div[contains(@class, \"col-md-4\")]//tr[.//span[text()=\"{idp_user_display_name_2}\"]]'\n", + " )\n", + " await expect(result_row).to_be_visible(timeout=transition_timeout)\n", + "\n", + "await run_pw(_step)" + ] + }, + { + "cell_type": "markdown", + "id": "step8-header", + "metadata": {}, + "source": [ + "## 「結果」の一覧の既存ユーザー2の左の「+」ボタンをクリックする\n", + "\n", + "「追加中」の一覧に既存ユーザー2が表示されること" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "step8", + "metadata": {}, + "outputs": [], + "source": [ + "async def _step(page):\n", + " result_row = page.locator('#addContributors').locator(\n", + " f'//div[contains(@class, \"col-md-4\")]//tr[.//span[text()=\"{idp_user_display_name_2}\"]]'\n", + " )\n", + " await result_row.locator('a.contrib-button.btn-success').click()\n", + "\n", + " adding_row = page.locator('#addContributors').locator(\n", + " f'//div[contains(@class, \"col-md-8\")]//tr[.//span[text()=\"{idp_user_display_name_2}\"]]'\n", + " )\n", + " await expect(adding_row).to_be_visible(timeout=transition_timeout)\n", + "\n", + "await run_pw(_step)" + ] + }, + { + "cell_type": "markdown", + "id": "step9-header", + "metadata": {}, + "source": [ + "## 「権限」を「読込み/書込み」に設定して、「追加」ボタンをクリックする\n", + "\n", + "「メンバー」画面に既存ユーザー2が追加されること" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "step9", + "metadata": {}, + "outputs": [], + "source": [ + "async def _step(page):\n", + " adding_row = page.locator('#addContributors').locator(\n", + " f'//div[contains(@class, \"col-md-8\")]//tr[.//span[text()=\"{idp_user_display_name_2}\"]]'\n", + " )\n", + " # メンバー追加ダイアログの permissionList は {value, text} のオブジェクト配列で optionsValue がないため、\n", + " #