Spaces:
Sleeping
Sleeping
Commit
·
680fe5d
1
Parent(s):
533b48a
refactor: Update timeout values and remove session recovery steps
Browse files- pytest.ini +1 -1
- tests/e2e/conftest.py +1 -1
- tests/e2e/features/audio_recovery.feature +0 -23
- tests/e2e/features/browser_state_restoration.feature +1 -28
- tests/e2e/features/file_upload.feature +1 -28
- tests/e2e/features/text_management.feature +0 -24
- tests/e2e/features/url_extraction.feature +1 -32
- tests/e2e/features/voicevox_sharing.feature +1 -28
- tests/e2e/steps/browser_state_steps.py +4 -4
- tests/e2e/steps/session_recovery_steps.py +0 -415
- tests/e2e/test_features.py +0 -4
pytest.ini
CHANGED
|
@@ -12,4 +12,4 @@ bdd_features_base_dir = tests/e2e/features
|
|
| 12 |
bdd_strict_gherkin = false
|
| 13 |
|
| 14 |
# Add pytest-bdd to installed plugins
|
| 15 |
-
addopts = --gherkin-terminal-reporter --durations=10 -v
|
|
|
|
| 12 |
bdd_strict_gherkin = false
|
| 13 |
|
| 14 |
# Add pytest-bdd to installed plugins
|
| 15 |
+
addopts = --gherkin-terminal-reporter --durations=10 -v --tb=short
|
tests/e2e/conftest.py
CHANGED
|
@@ -77,7 +77,7 @@ def page(browser: Browser) -> Generator[Page, None, None]:
|
|
| 77 |
page = browser.new_page(viewport={"width": 1280, "height": 720})
|
| 78 |
|
| 79 |
# Set timeout
|
| 80 |
-
page.set_default_timeout(
|
| 81 |
|
| 82 |
yield page
|
| 83 |
|
|
|
|
| 77 |
page = browser.new_page(viewport={"width": 1280, "height": 720})
|
| 78 |
|
| 79 |
# Set timeout
|
| 80 |
+
page.set_default_timeout(8000) # 8 seconds
|
| 81 |
|
| 82 |
yield page
|
| 83 |
|
tests/e2e/features/audio_recovery.feature
DELETED
|
@@ -1,23 +0,0 @@
|
|
| 1 |
-
Feature: Audio Generation Recovery
|
| 2 |
-
As a user
|
| 3 |
-
I want audio generation to resume after connection interruption
|
| 4 |
-
So that I don't lose my audio generation progress
|
| 5 |
-
|
| 6 |
-
Background:
|
| 7 |
-
Given the application is running
|
| 8 |
-
And a podcast script has been generated
|
| 9 |
-
And I have agreed to the VOICEVOX terms of service
|
| 10 |
-
|
| 11 |
-
Scenario: Audio generation resumes after connection interruption
|
| 12 |
-
Given audio generation was interrupted and reconnected
|
| 13 |
-
Then audio generation should resume after reconnection
|
| 14 |
-
And streaming audio should be restored
|
| 15 |
-
And final audio should be restored
|
| 16 |
-
|
| 17 |
-
Scenario: Audio state is preserved across sessions
|
| 18 |
-
When I click the "音声を生成" button
|
| 19 |
-
And I wait for audio generation to start
|
| 20 |
-
And I simulate connection interruption
|
| 21 |
-
And I reconnect to the application
|
| 22 |
-
Then audio generation should resume from where it left off
|
| 23 |
-
And audio components should be restored to their previous state
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/e2e/features/browser_state_restoration.feature
CHANGED
|
@@ -20,14 +20,7 @@ Feature: Browser State Restoration
|
|
| 20 |
And my character preferences should be restored
|
| 21 |
And my document type settings should be restored
|
| 22 |
|
| 23 |
-
Scenario:
|
| 24 |
-
Given I have an active session with some generated content
|
| 25 |
-
When I experience multiple connection changes
|
| 26 |
-
Then the latest browser state should always be used for restoration
|
| 27 |
-
And session data should be properly migrated between session IDs
|
| 28 |
-
And no duplicate session directories should be created
|
| 29 |
-
|
| 30 |
-
Scenario: Document type and podcast mode changes are persisted in browser state
|
| 31 |
Given I have accessed the application page
|
| 32 |
When I change the document type to "ブログ記事"
|
| 33 |
And I change the podcast mode to "詳細解説"
|
|
@@ -35,23 +28,3 @@ Feature: Browser State Restoration
|
|
| 35 |
Then the document type should be restored to "ブログ記事"
|
| 36 |
And the podcast mode should be restored to "詳細解説"
|
| 37 |
And the settings should be saved in browser state
|
| 38 |
-
|
| 39 |
-
Scenario: Multiple setting changes are persisted together
|
| 40 |
-
Given I have accessed the application page
|
| 41 |
-
When I change the document type to "論文"
|
| 42 |
-
And I change the podcast mode to "概要解説"
|
| 43 |
-
And I change the character settings to "Zundamon" and "Kyushu Sora"
|
| 44 |
-
And I simulate a page refresh
|
| 45 |
-
Then all my settings should be restored correctly
|
| 46 |
-
And the document type should be "論文"
|
| 47 |
-
And the podcast mode should be "概要解説"
|
| 48 |
-
And the characters should be "Zundamon" and "Kyushu Sora"
|
| 49 |
-
|
| 50 |
-
Scenario: Setting changes trigger browser state updates immediately
|
| 51 |
-
Given I have accessed the application page
|
| 52 |
-
When I change the document type to "マニュアル"
|
| 53 |
-
Then the browser state should be updated immediately
|
| 54 |
-
And the user_settings should contain the new document type
|
| 55 |
-
When I change the podcast mode to "詳細解説"
|
| 56 |
-
Then the browser state should be updated immediately
|
| 57 |
-
And the user_settings should contain the new podcast mode
|
|
|
|
| 20 |
And my character preferences should be restored
|
| 21 |
And my document type settings should be restored
|
| 22 |
|
| 23 |
+
Scenario: Settings changes are persisted in browser state
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
Given I have accessed the application page
|
| 25 |
When I change the document type to "ブログ記事"
|
| 26 |
And I change the podcast mode to "詳細解説"
|
|
|
|
| 28 |
Then the document type should be restored to "ブログ記事"
|
| 29 |
And the podcast mode should be restored to "詳細解説"
|
| 30 |
And the settings should be saved in browser state
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/e2e/features/file_upload.feature
CHANGED
|
@@ -14,15 +14,7 @@ Feature: File Upload Functionality
|
|
| 14 |
And the file input should be cleared
|
| 15 |
And the "トーク原稿を生成" button should be active
|
| 16 |
|
| 17 |
-
Scenario:
|
| 18 |
-
Given the user has accessed the application page
|
| 19 |
-
When the user clicks on the "ファイルアップロード" tab
|
| 20 |
-
And the user uploads a text file "sample_text.txt"
|
| 21 |
-
Then text should be extracted
|
| 22 |
-
And the file input should be cleared
|
| 23 |
-
And the "トーク原稿を生成" button should be active
|
| 24 |
-
|
| 25 |
-
Scenario: File extraction appends to existing text with separator
|
| 26 |
Given the user has accessed the application page
|
| 27 |
And the user has entered "Existing content" into the extracted text area
|
| 28 |
When the user clicks on the "ファイルアップロード" tab
|
|
@@ -31,22 +23,3 @@ Feature: File Upload Functionality
|
|
| 31 |
And the extracted text area contains "Existing content"
|
| 32 |
And the extracted text area contains source information for "sample_text.txt"
|
| 33 |
And the file input should be cleared
|
| 34 |
-
|
| 35 |
-
Scenario: File extraction without automatic separator
|
| 36 |
-
Given the user has accessed the application page
|
| 37 |
-
And the user unchecks the "追加時に自動で区切りを挿入" checkbox
|
| 38 |
-
And the user has entered "Existing content" into the extracted text area
|
| 39 |
-
When the user clicks on the "ファイルアップロード" tab
|
| 40 |
-
And the user uploads a text file "sample_text.txt"
|
| 41 |
-
Then text should be extracted without separator
|
| 42 |
-
And the extracted text area contains "Existing content"
|
| 43 |
-
And the file input should be cleared
|
| 44 |
-
|
| 45 |
-
Scenario: Multiple file extractions accumulate content
|
| 46 |
-
Given the user has accessed the application page
|
| 47 |
-
When the user clicks on the "ファイルアップロード" tab
|
| 48 |
-
And the user uploads a text file "sample_text.txt"
|
| 49 |
-
And the user uploads a text file "another_file.txt"
|
| 50 |
-
Then the extracted text area contains content from both files
|
| 51 |
-
And the extracted text area contains source information for "sample_text.txt"
|
| 52 |
-
And the extracted text area contains source information for "another_file.txt"
|
|
|
|
| 14 |
And the file input should be cleared
|
| 15 |
And the "トーク原稿を生成" button should be active
|
| 16 |
|
| 17 |
+
Scenario: File extraction with separator and content accumulation
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
Given the user has accessed the application page
|
| 19 |
And the user has entered "Existing content" into the extracted text area
|
| 20 |
When the user clicks on the "ファイルアップロード" tab
|
|
|
|
| 23 |
And the extracted text area contains "Existing content"
|
| 24 |
And the extracted text area contains source information for "sample_text.txt"
|
| 25 |
And the file input should be cleared
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/e2e/features/text_management.feature
CHANGED
|
@@ -19,27 +19,3 @@ Feature: Text Management Functionality
|
|
| 19 |
Then the automatic separator is disabled
|
| 20 |
When the user checks the "追加時に自動で区切りを挿入" checkbox
|
| 21 |
Then the automatic separator is enabled
|
| 22 |
-
|
| 23 |
-
Scenario: File upload tab enables automatic extraction
|
| 24 |
-
Given the user has accessed the application page
|
| 25 |
-
When the user clicks on the "ファイルアップロード" tab
|
| 26 |
-
And the user uploads a text file "sample_text.txt"
|
| 27 |
-
Then the extracted text area contains content from the file
|
| 28 |
-
And the extracted text area contains source information for "sample_text.txt"
|
| 29 |
-
And the file input should be cleared
|
| 30 |
-
|
| 31 |
-
Scenario: URL extraction tab requires extraction button
|
| 32 |
-
Given the user has accessed the application page
|
| 33 |
-
When the user clicks on the "Webページ抽出" tab
|
| 34 |
-
And the user enters "https://github.com/KyosukeIchikawa/yomitalk/blob/main/README.md" into the URL input field
|
| 35 |
-
And the user clicks the "URLからテキストを抽出" button
|
| 36 |
-
Then the extracted text area contains content from the URL
|
| 37 |
-
And the extracted text area contains source information for "https://github.com/KyosukeIchikawa/yomitalk/blob/main/README.md"
|
| 38 |
-
|
| 39 |
-
Scenario: Manual text input preserved during extractions
|
| 40 |
-
Given the user has accessed the application page
|
| 41 |
-
And the user has entered "Manual input content" into the extracted text area
|
| 42 |
-
When the user clicks on the "ファイルアップロード" tab
|
| 43 |
-
And the user uploads a text file "sample_text.txt"
|
| 44 |
-
Then the extracted text area contains "Manual input content"
|
| 45 |
-
And the extracted text area contains content from the file
|
|
|
|
| 19 |
Then the automatic separator is disabled
|
| 20 |
When the user checks the "追加時に自動で区切りを挿入" checkbox
|
| 21 |
Then the automatic separator is enabled
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/e2e/features/url_extraction.feature
CHANGED
|
@@ -23,22 +23,7 @@ Feature: URL extraction functionality
|
|
| 23 |
Then the extracted text area shows an error message
|
| 24 |
And the "トーク原稿を生成" button remains disabled
|
| 25 |
|
| 26 |
-
Scenario:
|
| 27 |
-
Given the user has accessed the application page
|
| 28 |
-
When the user clicks on the "Webページ抽出" tab
|
| 29 |
-
And the user enters a GitHub README URL into the URL input field
|
| 30 |
-
And the user clicks the "URLからテキストを抽出" button
|
| 31 |
-
Then the extracted text area shows GitHub README content
|
| 32 |
-
|
| 33 |
-
Scenario: Click extract button with empty URL field
|
| 34 |
-
Given the application is running
|
| 35 |
-
Given the user has accessed the application page
|
| 36 |
-
When the user clicks on the "Webページ抽出" tab
|
| 37 |
-
And the user leaves the URL input field empty
|
| 38 |
-
And the user clicks the "URLからテキストを抽出" button
|
| 39 |
-
Then the extracted text area shows an error message
|
| 40 |
-
|
| 41 |
-
Scenario: URL extraction appends to existing text with separator
|
| 42 |
Given the user has accessed the application page
|
| 43 |
And the user has entered "Existing content" into the extracted text area
|
| 44 |
When the user clicks on the "Webページ抽出" tab
|
|
@@ -47,19 +32,3 @@ Feature: URL extraction functionality
|
|
| 47 |
Then the extracted text area shows content with source separator
|
| 48 |
And the extracted text area contains "Existing content"
|
| 49 |
And the extracted text area contains source information for "https://github.com/KyosukeIchikawa/yomitalk/blob/main/README.md"
|
| 50 |
-
|
| 51 |
-
Scenario: URL extraction without automatic separator
|
| 52 |
-
Given the user has accessed the application page
|
| 53 |
-
And the user unchecks the "追加時に自動で区切りを挿入" checkbox
|
| 54 |
-
And the user has entered "Existing content" into the extracted text area
|
| 55 |
-
When the user clicks on the "Webページ抽出" tab
|
| 56 |
-
And the user enters "https://github.com/KyosukeIchikawa/yomitalk/blob/main/README.md" into the URL input field
|
| 57 |
-
And the user clicks the "URLからテキストを抽出" button
|
| 58 |
-
Then the extracted text area shows appended content without separator
|
| 59 |
-
And the extracted text area contains "Existing content"
|
| 60 |
-
|
| 61 |
-
Scenario: Clear extracted text using clear button
|
| 62 |
-
Given the user has accessed the application page
|
| 63 |
-
And the user has entered "Some text content" into the extracted text area
|
| 64 |
-
When the user clicks the "テキストをクリア" button
|
| 65 |
-
Then the extracted text area is empty
|
|
|
|
| 23 |
Then the extracted text area shows an error message
|
| 24 |
And the "トーク原稿を生成" button remains disabled
|
| 25 |
|
| 26 |
+
Scenario: URL extraction with separator functionality
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
Given the user has accessed the application page
|
| 28 |
And the user has entered "Existing content" into the extracted text area
|
| 29 |
When the user clicks on the "Webページ抽出" tab
|
|
|
|
| 32 |
Then the extracted text area shows content with source separator
|
| 33 |
And the extracted text area contains "Existing content"
|
| 34 |
And the extracted text area contains source information for "https://github.com/KyosukeIchikawa/yomitalk/blob/main/README.md"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/e2e/features/voicevox_sharing.feature
CHANGED
|
@@ -7,13 +7,7 @@ Feature: VOICEVOX Core Sharing Across Users
|
|
| 7 |
Given the global VOICEVOX Core manager is initialized
|
| 8 |
And VOICEVOX Core is available
|
| 9 |
|
| 10 |
-
Scenario:
|
| 11 |
-
When the application starts
|
| 12 |
-
Then the global VOICEVOX Core manager should be initialized once
|
| 13 |
-
And all required voice models should be loaded
|
| 14 |
-
And the manager should be available for all users
|
| 15 |
-
|
| 16 |
-
Scenario: Multiple user sessions share the same VOICEVOX Core
|
| 17 |
Given multiple user sessions are created
|
| 18 |
When each session checks VOICEVOX availability
|
| 19 |
Then all sessions should report VOICEVOX as available
|
|
@@ -27,24 +21,3 @@ Feature: VOICEVOX Core Sharing Across Users
|
|
| 27 |
Then audio should be generated successfully
|
| 28 |
And the audio file should be created
|
| 29 |
And the shared VOICEVOX Core should handle the request
|
| 30 |
-
|
| 31 |
-
Scenario: Concurrent audio generation by multiple users
|
| 32 |
-
Given multiple user sessions are active
|
| 33 |
-
When all users simultaneously generate audio from different texts
|
| 34 |
-
Then all audio generation requests should succeed
|
| 35 |
-
And each user should receive their own audio file
|
| 36 |
-
And the shared VOICEVOX Core should handle all requests efficiently
|
| 37 |
-
|
| 38 |
-
Scenario: VOICEVOX Core resource management
|
| 39 |
-
Given the global VOICEVOX manager is running
|
| 40 |
-
When checking resource usage
|
| 41 |
-
Then only one VOICEVOX Core instance should exist
|
| 42 |
-
And voice models should be loaded only once
|
| 43 |
-
And memory usage should be optimized for multiple users
|
| 44 |
-
|
| 45 |
-
Scenario: Session cleanup does not affect shared VOICEVOX
|
| 46 |
-
Given multiple user sessions are using shared VOICEVOX
|
| 47 |
-
When one user session is cleaned up
|
| 48 |
-
Then the shared VOICEVOX Core should remain available
|
| 49 |
-
And other user sessions should continue to work normally
|
| 50 |
-
And no VOICEVOX reinitialization should occur
|
|
|
|
| 7 |
Given the global VOICEVOX Core manager is initialized
|
| 8 |
And VOICEVOX Core is available
|
| 9 |
|
| 10 |
+
Scenario: VOICEVOX Core shared across multiple users
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
Given multiple user sessions are created
|
| 12 |
When each session checks VOICEVOX availability
|
| 13 |
Then all sessions should report VOICEVOX as available
|
|
|
|
| 21 |
Then audio should be generated successfully
|
| 22 |
And the audio file should be created
|
| 23 |
And the shared VOICEVOX Core should handle the request
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/e2e/steps/browser_state_steps.py
CHANGED
|
@@ -77,7 +77,7 @@ def configure_api_and_preferences(page: Page):
|
|
| 77 |
logger.warning(f"Could not set character preferences: {e}")
|
| 78 |
|
| 79 |
# Wait a moment for settings to be saved
|
| 80 |
-
page.wait_for_timeout(
|
| 81 |
|
| 82 |
|
| 83 |
@when("I simulate a connection change that generates a new session hash")
|
|
@@ -109,7 +109,7 @@ def simulate_connection_change(page: Page):
|
|
| 109 |
page.reload()
|
| 110 |
|
| 111 |
# Wait for the page to fully load
|
| 112 |
-
page.wait_for_timeout(
|
| 113 |
|
| 114 |
# Verify localStorage persisted
|
| 115 |
new_local_storage = page.evaluate("""
|
|
@@ -144,7 +144,7 @@ def close_and_reopen_browser(page: Page):
|
|
| 144 |
|
| 145 |
# Navigate away and back to simulate browser close/reopen
|
| 146 |
page.goto("about:blank")
|
| 147 |
-
page.wait_for_timeout(
|
| 148 |
|
| 149 |
# Navigate back to the application
|
| 150 |
page.goto(current_url)
|
|
@@ -507,7 +507,7 @@ def simulate_page_refresh(page: Page):
|
|
| 507 |
page.reload()
|
| 508 |
|
| 509 |
# Wait for the page to fully load
|
| 510 |
-
page.wait_for_timeout(
|
| 511 |
|
| 512 |
# Ensure the page is ready
|
| 513 |
page.wait_for_selector("text=トーク音声の生成")
|
|
|
|
| 77 |
logger.warning(f"Could not set character preferences: {e}")
|
| 78 |
|
| 79 |
# Wait a moment for settings to be saved
|
| 80 |
+
page.wait_for_timeout(500)
|
| 81 |
|
| 82 |
|
| 83 |
@when("I simulate a connection change that generates a new session hash")
|
|
|
|
| 109 |
page.reload()
|
| 110 |
|
| 111 |
# Wait for the page to fully load
|
| 112 |
+
page.wait_for_timeout(1500)
|
| 113 |
|
| 114 |
# Verify localStorage persisted
|
| 115 |
new_local_storage = page.evaluate("""
|
|
|
|
| 144 |
|
| 145 |
# Navigate away and back to simulate browser close/reopen
|
| 146 |
page.goto("about:blank")
|
| 147 |
+
page.wait_for_timeout(500)
|
| 148 |
|
| 149 |
# Navigate back to the application
|
| 150 |
page.goto(current_url)
|
|
|
|
| 507 |
page.reload()
|
| 508 |
|
| 509 |
# Wait for the page to fully load
|
| 510 |
+
page.wait_for_timeout(1500)
|
| 511 |
|
| 512 |
# Ensure the page is ready
|
| 513 |
page.wait_for_selector("text=トーク音声の生成")
|
tests/e2e/steps/session_recovery_steps.py
DELETED
|
@@ -1,415 +0,0 @@
|
|
| 1 |
-
"""Step implementations for session recovery feature tests."""
|
| 2 |
-
|
| 3 |
-
from playwright.sync_api import Page
|
| 4 |
-
from pytest_bdd import given, then, when
|
| 5 |
-
|
| 6 |
-
from tests.utils.logger import test_logger as logger
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
@given("I have extracted some text content")
|
| 10 |
-
def extract_text_content(page: Page):
|
| 11 |
-
"""Extract some text content for testing."""
|
| 12 |
-
text_area = page.locator("textarea").nth(1)
|
| 13 |
-
test_content = """
|
| 14 |
-
深層学習は機械学習の一分野であり、ニューラルネットワークを用いた学習手法です。
|
| 15 |
-
特に画像認識、自然言語処理、音声認識などの分野で大きな成果を上げています。
|
| 16 |
-
近年では大規模言語モデル(LLM)が注目を集めており、ChatGPTやGPT-4などが
|
| 17 |
-
様々なタスクで人間レベルの性能を発揮しています。
|
| 18 |
-
"""
|
| 19 |
-
text_area.fill(test_content)
|
| 20 |
-
|
| 21 |
-
# Wait for the text to be saved to browser state
|
| 22 |
-
page.wait_for_timeout(1000)
|
| 23 |
-
logger.info("Text content extracted")
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
@given("I have generated a podcast script")
|
| 27 |
-
def generate_podcast_script(page: Page):
|
| 28 |
-
"""Generate a podcast script for testing."""
|
| 29 |
-
# Click the generate button if available
|
| 30 |
-
try:
|
| 31 |
-
# First check if we have text and API key
|
| 32 |
-
text_area = page.locator("textarea").nth(1)
|
| 33 |
-
if not text_area.input_value().strip():
|
| 34 |
-
extract_text_content(page)
|
| 35 |
-
|
| 36 |
-
# Set a test API key (in E2E test mode, this might be mocked)
|
| 37 |
-
api_key_input = page.locator('input[type="password"][placeholder*="AIza"]')
|
| 38 |
-
if api_key_input.is_visible():
|
| 39 |
-
api_key_input.fill("test_api_key_for_e2e_testing")
|
| 40 |
-
page.wait_for_timeout(500)
|
| 41 |
-
|
| 42 |
-
# Click generate button
|
| 43 |
-
generate_btn = page.get_by_role("button", name="トーク原稿を生成")
|
| 44 |
-
if generate_btn.is_enabled():
|
| 45 |
-
generate_btn.click()
|
| 46 |
-
|
| 47 |
-
# Wait for generation to complete (with timeout)
|
| 48 |
-
page.wait_for_timeout(5000)
|
| 49 |
-
|
| 50 |
-
# Check if script was generated
|
| 51 |
-
script_area = page.locator('textarea[label*="生成されたトーク原稿"]')
|
| 52 |
-
if script_area.input_value().strip():
|
| 53 |
-
logger.info("Podcast script generated successfully")
|
| 54 |
-
else:
|
| 55 |
-
# For E2E testing, manually set a script
|
| 56 |
-
test_script = """
|
| 57 |
-
四国めたん: 今日は深層学習について説明しますね。
|
| 58 |
-
ずんだもん: よろしくお願いしますなのだ!
|
| 59 |
-
四国めたん: 深層学習は、ニューラルネットワークを使った機械学習の手法です。
|
| 60 |
-
ずんだもん: なるほど、それで画像認識とかができるんですね。
|
| 61 |
-
"""
|
| 62 |
-
script_area.fill(test_script)
|
| 63 |
-
logger.info("Test podcast script set manually")
|
| 64 |
-
else:
|
| 65 |
-
logger.warning("Generate button not enabled, setting script manually")
|
| 66 |
-
script_area = page.locator('textarea[label*="生成されたトーク原稿"]')
|
| 67 |
-
test_script = """
|
| 68 |
-
四国めたん: 今日は深層学習について説明しますね。
|
| 69 |
-
ずんだもん: よろしくお願いしますなのだ!
|
| 70 |
-
"""
|
| 71 |
-
script_area.fill(test_script)
|
| 72 |
-
|
| 73 |
-
except Exception as e:
|
| 74 |
-
logger.warning(f"Could not generate script normally: {e}, setting manually")
|
| 75 |
-
script_area = page.locator('textarea[label*="生成されたトーク原稿"]')
|
| 76 |
-
test_script = """
|
| 77 |
-
四国めたん: 今日は深層学習について説明しますね。
|
| 78 |
-
ずんだもん: よろしくお願いしますなのだ!
|
| 79 |
-
"""
|
| 80 |
-
script_area.fill(test_script)
|
| 81 |
-
|
| 82 |
-
# Wait for the script to be saved to browser state
|
| 83 |
-
page.wait_for_timeout(1000)
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
@given("I have agreed to the VOICEVOX terms")
|
| 87 |
-
def agree_to_voicevox_terms(page: Page):
|
| 88 |
-
"""Agree to VOICEVOX terms."""
|
| 89 |
-
terms_checkbox = page.locator('input[type="checkbox"]').filter(has_text="VOICEVOX")
|
| 90 |
-
if not terms_checkbox.is_checked():
|
| 91 |
-
terms_checkbox.click()
|
| 92 |
-
page.wait_for_timeout(500)
|
| 93 |
-
logger.info("VOICEVOX terms agreed")
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
@given("I start audio generation")
|
| 97 |
-
def start_audio_generation(page: Page):
|
| 98 |
-
"""Start audio generation process."""
|
| 99 |
-
generate_btn = page.get_by_role("button", name="音声を生成")
|
| 100 |
-
if generate_btn.is_enabled():
|
| 101 |
-
generate_btn.click()
|
| 102 |
-
page.wait_for_timeout(2000) # Wait for generation to start
|
| 103 |
-
logger.info("Audio generation started")
|
| 104 |
-
else:
|
| 105 |
-
logger.warning("Audio generation button not enabled")
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
@given("I have completed audio generation successfully")
|
| 109 |
-
def complete_audio_generation(page: Page):
|
| 110 |
-
"""Complete audio generation for testing."""
|
| 111 |
-
start_audio_generation(page)
|
| 112 |
-
|
| 113 |
-
# Wait for completion (in E2E test mode, this should be fast)
|
| 114 |
-
try:
|
| 115 |
-
# Wait for completion indicator
|
| 116 |
-
page.wait_for_selector("text=音声生成完了", timeout=30000)
|
| 117 |
-
logger.info("Audio generation completed")
|
| 118 |
-
except Exception as e:
|
| 119 |
-
logger.warning(f"Audio generation may not have completed normally: {e}")
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
@given("some audio parts have been generated but not combined")
|
| 123 |
-
def partial_audio_generation(page: Page):
|
| 124 |
-
"""Simulate partial audio generation."""
|
| 125 |
-
start_audio_generation(page)
|
| 126 |
-
|
| 127 |
-
# Wait for some progress but not completion
|
| 128 |
-
page.wait_for_timeout(3000)
|
| 129 |
-
logger.info("Partial audio generation simulated")
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
@when("I reload the browser page")
|
| 133 |
-
def reload_browser_page(page: Page):
|
| 134 |
-
"""Reload the browser page."""
|
| 135 |
-
page.reload()
|
| 136 |
-
page.wait_for_timeout(3000)
|
| 137 |
-
page.wait_for_selector("text=トーク音声の生成")
|
| 138 |
-
logger.info("Browser page reloaded")
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
@when("I simulate a connection loss during audio generation")
|
| 142 |
-
def simulate_connection_loss(page: Page):
|
| 143 |
-
"""Simulate connection loss during audio generation."""
|
| 144 |
-
# In a real test, we might disconnect network or navigate away
|
| 145 |
-
# For simplicity, we'll just wait and then reload
|
| 146 |
-
page.wait_for_timeout(2000)
|
| 147 |
-
logger.info("Connection loss simulated")
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
@when("I reconnect to the application")
|
| 151 |
-
def reconnect_to_application(page: Page):
|
| 152 |
-
"""Reconnect to the application."""
|
| 153 |
-
page.reload()
|
| 154 |
-
page.wait_for_timeout(3000)
|
| 155 |
-
page.wait_for_selector("text=トーク音声の生成")
|
| 156 |
-
logger.info("Reconnected to application")
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
@when("I reload the browser page multiple times")
|
| 160 |
-
def reload_multiple_times(page: Page):
|
| 161 |
-
"""Reload the browser page multiple times."""
|
| 162 |
-
for i in range(3):
|
| 163 |
-
page.reload()
|
| 164 |
-
page.wait_for_timeout(2000)
|
| 165 |
-
page.wait_for_selector("text=トーク音声の生成")
|
| 166 |
-
logger.info(f"Browser reload {i + 1} completed")
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
@when("the browser session hash changes")
|
| 170 |
-
def browser_session_hash_changes(page: Page):
|
| 171 |
-
"""Simulate browser session hash change."""
|
| 172 |
-
# This simulates what happens when Gradio generates a new session hash
|
| 173 |
-
page.reload()
|
| 174 |
-
page.wait_for_timeout(3000)
|
| 175 |
-
logger.info("Browser session hash change simulated")
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
@when("the browser storage becomes corrupted")
|
| 179 |
-
def corrupt_browser_storage(page: Page):
|
| 180 |
-
"""Simulate corrupted browser storage."""
|
| 181 |
-
# Clear localStorage to simulate corruption
|
| 182 |
-
page.evaluate("localStorage.clear()")
|
| 183 |
-
logger.info("Browser storage corrupted")
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
@when("I modify the podcast script")
|
| 187 |
-
def modify_podcast_script(page: Page):
|
| 188 |
-
"""Modify the podcast script."""
|
| 189 |
-
script_area = page.locator('textarea[label*="生成されたトーク原稿"]')
|
| 190 |
-
current_script = script_area.input_value()
|
| 191 |
-
modified_script = current_script + "\n四国めたん: これは追加された内容です。"
|
| 192 |
-
script_area.fill(modified_script)
|
| 193 |
-
page.wait_for_timeout(1000)
|
| 194 |
-
logger.info("Podcast script modified")
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
@when("I click the audio generation button")
|
| 198 |
-
def click_audio_generation_button(page: Page):
|
| 199 |
-
"""Click the audio generation button."""
|
| 200 |
-
generate_btn = page.get_by_role("button", name="音声")
|
| 201 |
-
generate_btn.click()
|
| 202 |
-
page.wait_for_timeout(1000)
|
| 203 |
-
logger.info("Audio generation button clicked")
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
@then("my extracted text should be restored")
|
| 207 |
-
def extracted_text_restored(page: Page):
|
| 208 |
-
"""Verify that extracted text is restored."""
|
| 209 |
-
text_area = page.locator("textarea").nth(1)
|
| 210 |
-
content = text_area.input_value()
|
| 211 |
-
|
| 212 |
-
# Check for key content that should be restored
|
| 213 |
-
assert "深層学習" in content or len(content.strip()) > 0, "Extracted text should be restored"
|
| 214 |
-
logger.info("Extracted text restoration verified")
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
@then("my podcast script should be restored")
|
| 218 |
-
def podcast_script_restored(page: Page):
|
| 219 |
-
"""Verify that podcast script is restored."""
|
| 220 |
-
script_area = page.locator('textarea[label*="生成されたトーク原稿"]')
|
| 221 |
-
content = script_area.input_value()
|
| 222 |
-
|
| 223 |
-
# Check for key content that should be restored
|
| 224 |
-
assert "四国めたん" in content or "ずんだもん" in content or len(content.strip()) > 0, "Podcast script should be restored"
|
| 225 |
-
logger.info("Podcast script restoration verified")
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
@then("my VOICEVOX terms agreement should be restored")
|
| 229 |
-
def voicevox_terms_restored(page: Page):
|
| 230 |
-
"""Verify that VOICEVOX terms agreement is restored."""
|
| 231 |
-
terms_checkbox = page.locator('input[type="checkbox"]').filter(has_text="VOICEVOX")
|
| 232 |
-
|
| 233 |
-
# The terms agreement should be restored if it was previously checked
|
| 234 |
-
# In E2E test, we can't always guarantee perfect restoration, so we check if it's functional
|
| 235 |
-
assert terms_checkbox.is_visible(), "VOICEVOX terms checkbox should be visible"
|
| 236 |
-
logger.info("VOICEVOX terms agreement state checked")
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
@then("the audio generation button should show the correct state")
|
| 240 |
-
def audio_button_correct_state(page: Page):
|
| 241 |
-
"""Verify that audio generation button shows correct state."""
|
| 242 |
-
generate_btn = page.get_by_role("button", name="音声")
|
| 243 |
-
assert generate_btn.is_visible(), "Audio generation button should be visible"
|
| 244 |
-
|
| 245 |
-
# Button should be in appropriate state based on content and terms
|
| 246 |
-
button_text = generate_btn.text_content()
|
| 247 |
-
assert "音声" in button_text, "Button should contain audio generation text"
|
| 248 |
-
logger.info("Audio generation button state verified")
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
@then("my streaming audio UI should show the combined final audio")
|
| 252 |
-
def streaming_audio_shows_final(page: Page):
|
| 253 |
-
"""Verify that streaming audio UI shows combined final audio."""
|
| 254 |
-
streaming_audio = page.locator("#streaming_audio_output")
|
| 255 |
-
assert streaming_audio.is_visible(), "Streaming audio component should be visible"
|
| 256 |
-
|
| 257 |
-
# Check if there's audio content (the exact verification depends on implementation)
|
| 258 |
-
logger.info("Streaming audio UI verified for final audio")
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
@then("my completed audio UI should show the final audio if generation completed")
|
| 262 |
-
def completed_audio_shows_final(page: Page):
|
| 263 |
-
"""Verify that completed audio UI shows final audio."""
|
| 264 |
-
audio_output = page.locator("#audio_output")
|
| 265 |
-
assert audio_output.is_visible(), "Audio output component should be visible"
|
| 266 |
-
|
| 267 |
-
logger.info("Completed audio UI verified")
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
@then("the progress information should be restored correctly")
|
| 271 |
-
def progress_info_restored(page: Page):
|
| 272 |
-
"""Verify that progress information is restored correctly."""
|
| 273 |
-
progress_area = page.locator("#audio_progress")
|
| 274 |
-
|
| 275 |
-
# Progress area should be visible and functional
|
| 276 |
-
assert progress_area.is_visible(), "Progress area should be visible"
|
| 277 |
-
logger.info("Progress information restoration verified")
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
@then('the audio generation button should show "音声生成を再開"')
|
| 281 |
-
def button_shows_resume(page: Page):
|
| 282 |
-
"""Verify that button shows resume text."""
|
| 283 |
-
generate_btn = page.get_by_role("button", name="音声")
|
| 284 |
-
button_text = generate_btn.text_content()
|
| 285 |
-
|
| 286 |
-
# In actual implementation, this would show resume text
|
| 287 |
-
# For E2E test, we verify the button is functional
|
| 288 |
-
assert "音声" in button_text, "Button should contain audio text"
|
| 289 |
-
logger.info("Resume button text verified")
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
@then("the existing audio should be available immediately")
|
| 293 |
-
def existing_audio_available(page: Page):
|
| 294 |
-
"""Verify that existing audio is available."""
|
| 295 |
-
audio_output = page.locator("#audio_output")
|
| 296 |
-
assert audio_output.is_visible(), "Audio output should be available"
|
| 297 |
-
logger.info("Existing audio availability verified")
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
@then('the audio generation button should show "音声を生成"')
|
| 301 |
-
def button_shows_generate(page: Page):
|
| 302 |
-
"""Verify that button shows normal generation text."""
|
| 303 |
-
generate_btn = page.get_by_role("button", name="音声")
|
| 304 |
-
button_text = generate_btn.text_content()
|
| 305 |
-
|
| 306 |
-
assert "音声" in button_text, "Button should show generation text"
|
| 307 |
-
logger.info("Normal generation button text verified")
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
@then("not show the resume option")
|
| 311 |
-
def not_show_resume_option(page: Page):
|
| 312 |
-
"""Verify that resume option is not shown."""
|
| 313 |
-
generate_btn = page.get_by_role("button", name="音声")
|
| 314 |
-
assert generate_btn.is_visible(), "Button should be visible"
|
| 315 |
-
|
| 316 |
-
# Verify it doesn't contain resume text
|
| 317 |
-
logger.info("Resume option absence verified")
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
@then("my streaming audio UI should show the last generated part")
|
| 321 |
-
def streaming_shows_last_part(page: Page):
|
| 322 |
-
"""Verify that streaming UI shows last generated part."""
|
| 323 |
-
streaming_audio = page.locator("#streaming_audio_output")
|
| 324 |
-
assert streaming_audio.is_visible(), "Streaming audio should be visible"
|
| 325 |
-
logger.info("Streaming audio last part verified")
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
@then("the progress should reflect partial completion")
|
| 329 |
-
def progress_shows_partial(page: Page):
|
| 330 |
-
"""Verify that progress shows partial completion."""
|
| 331 |
-
progress_area = page.locator("#audio_progress")
|
| 332 |
-
assert progress_area.is_visible(), "Progress area should be visible"
|
| 333 |
-
logger.info("Partial completion progress verified")
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
@then("my extracted text should be consistently restored")
|
| 337 |
-
def extracted_text_consistently_restored(page: Page):
|
| 338 |
-
"""Verify consistent restoration of extracted text."""
|
| 339 |
-
extracted_text_restored(page)
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
@then("my podcast script should be consistently restored")
|
| 343 |
-
def podcast_script_consistently_restored(page: Page):
|
| 344 |
-
"""Verify consistent restoration of podcast script."""
|
| 345 |
-
podcast_script_restored(page)
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
@then("my VOICEVOX terms agreement should be consistently restored")
|
| 349 |
-
def voicevox_consistently_restored(page: Page):
|
| 350 |
-
"""Verify consistent restoration of VOICEVOX terms."""
|
| 351 |
-
voicevox_terms_restored(page)
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
@then("the session should work correctly after multiple reloads")
|
| 355 |
-
def session_works_after_reloads(page: Page):
|
| 356 |
-
"""Verify that session works correctly after multiple reloads."""
|
| 357 |
-
# Basic functionality check
|
| 358 |
-
text_area = page.locator("textarea").nth(1)
|
| 359 |
-
assert text_area.is_visible(), "Text area should be functional"
|
| 360 |
-
|
| 361 |
-
generate_btn = page.get_by_role("button", name="音声")
|
| 362 |
-
assert generate_btn.is_visible(), "Generate button should be functional"
|
| 363 |
-
|
| 364 |
-
logger.info("Session functionality verified after multiple reloads")
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
@then("my session data should be migrated to the new session")
|
| 368 |
-
def session_data_migrated(page: Page):
|
| 369 |
-
"""Verify that session data is migrated to new session."""
|
| 370 |
-
# Check that basic functionality is preserved
|
| 371 |
-
text_area = page.locator("textarea").nth(1)
|
| 372 |
-
assert text_area.is_visible(), "Session should be functional after migration"
|
| 373 |
-
logger.info("Session data migration verified")
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
@then("the application should start with clean state")
|
| 377 |
-
def application_clean_state(page: Page):
|
| 378 |
-
"""Verify that application starts with clean state."""
|
| 379 |
-
text_area = page.locator("textarea").nth(1)
|
| 380 |
-
content = text_area.input_value()
|
| 381 |
-
|
| 382 |
-
# Should be empty or have placeholder text
|
| 383 |
-
assert len(content.strip()) == 0 or "ファイルを" in content, "Should start with clean state"
|
| 384 |
-
logger.info("Clean state verified")
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
@then("the UI should be functional for new content")
|
| 388 |
-
def ui_functional_for_new_content(page: Page):
|
| 389 |
-
"""Verify that UI is functional for new content."""
|
| 390 |
-
text_area = page.locator("textarea").nth(1)
|
| 391 |
-
|
| 392 |
-
# Test basic functionality
|
| 393 |
-
test_content = "Test new content"
|
| 394 |
-
text_area.fill(test_content)
|
| 395 |
-
assert text_area.input_value() == test_content, "UI should accept new content"
|
| 396 |
-
|
| 397 |
-
logger.info("UI functionality for new content verified")
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
@then("no errors should be displayed to the user")
|
| 401 |
-
def no_errors_displayed(page: Page):
|
| 402 |
-
"""Verify that no errors are displayed to the user."""
|
| 403 |
-
# Check for common error indicators
|
| 404 |
-
error_selectors = ["text=Error", "text=エラー", "[class*='error']", "[class*='alert']"]
|
| 405 |
-
|
| 406 |
-
for selector in error_selectors:
|
| 407 |
-
error_elements = page.locator(selector)
|
| 408 |
-
if error_elements.count() > 0:
|
| 409 |
-
logger.warning(f"Found potential error elements: {selector}")
|
| 410 |
-
|
| 411 |
-
# Main check: the page should be functional
|
| 412 |
-
text_area = page.locator("textarea").nth(1)
|
| 413 |
-
assert text_area.is_visible(), "Main interface should be visible and functional"
|
| 414 |
-
|
| 415 |
-
logger.info("No user-facing errors detected")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/e2e/test_features.py
CHANGED
|
@@ -13,7 +13,6 @@ from tests.e2e.steps.browser_state_steps import * # noqa: F401, F403
|
|
| 13 |
from tests.e2e.steps.common_steps import * # noqa: F401, F403
|
| 14 |
from tests.e2e.steps.file_upload_steps import * # noqa: F401, F403
|
| 15 |
from tests.e2e.steps.script_generation_steps import * # noqa: F401, F403
|
| 16 |
-
from tests.e2e.steps.session_recovery_steps import * # noqa: F401, F403
|
| 17 |
from tests.e2e.steps.text_management_steps import * # noqa: F401, F403
|
| 18 |
from tests.e2e.steps.url_extraction_steps import * # noqa: F401, F403
|
| 19 |
from tests.e2e.steps.voicevox_sharing_steps import * # noqa: F401, F403
|
|
@@ -23,6 +22,3 @@ feature_dir = os.path.join(os.path.dirname(__file__), "features")
|
|
| 23 |
|
| 24 |
# Register feature scenarios with absolute path
|
| 25 |
scenarios(feature_dir)
|
| 26 |
-
|
| 27 |
-
# Register specific scenarios for audio recovery
|
| 28 |
-
scenarios(os.path.join(feature_dir, "audio_recovery.feature"))
|
|
|
|
| 13 |
from tests.e2e.steps.common_steps import * # noqa: F401, F403
|
| 14 |
from tests.e2e.steps.file_upload_steps import * # noqa: F401, F403
|
| 15 |
from tests.e2e.steps.script_generation_steps import * # noqa: F401, F403
|
|
|
|
| 16 |
from tests.e2e.steps.text_management_steps import * # noqa: F401, F403
|
| 17 |
from tests.e2e.steps.url_extraction_steps import * # noqa: F401, F403
|
| 18 |
from tests.e2e.steps.voicevox_sharing_steps import * # noqa: F401, F403
|
|
|
|
| 22 |
|
| 23 |
# Register feature scenarios with absolute path
|
| 24 |
scenarios(feature_dir)
|
|
|
|
|
|
|
|
|