Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
dcfb2a4
add TestSidebar and MainContent components to improve test management UI
raffaelecarelle Dec 4, 2025
d3a9627
update XPath query for file node selection to ensure better compatibi…
raffaelecarelle Dec 4, 2025
8fc58a1
remove unnecessary console logs, enhance syntax highlighting in cover…
raffaelecarelle Dec 4, 2025
fdbd51e
refactor MainContent component to simplify token class logic and remo…
raffaelecarelle Dec 5, 2025
02f178e
add IndividualTestResults and GroupedTestResults components, streamli…
raffaelecarelle Dec 5, 2025
b1a2ee5
remove redundant <package> wrappers from CoverageTest XML test data
raffaelecarelle Dec 5, 2025
d8e34ff
modularize TestSidebar by splitting into reusable components for impr…
raffaelecarelle Dec 5, 2025
f87b601
modularize Header by splitting into reusable components for improved …
raffaelecarelle Dec 5, 2025
5e04574
refactor TestSidebar to integrate filtered suites logic, remove redun…
raffaelecarelle Dec 5, 2025
909d897
extend ResultsSummary props to include 'store' for enhanced data hand…
raffaelecarelle Dec 5, 2025
d1a53f4
prevent duplicate test runs by adding early return check for 'isStart…
raffaelecarelle Dec 5, 2025
2ec2576
replace inline Tailwind CSS in `input.css` with modular Vue component…
raffaelecarelle Dec 5, 2025
cf4e3d3
update Router to handle Vite's manifest and scripts, fallback to deve…
raffaelecarelle Dec 5, 2025
3c6027b
add Vite's manifest and related assets (JSON, SVGs, CSS) to public bu…
raffaelecarelle Dec 5, 2025
7f8c422
remove unused SVG favicon assets and related build files to clean up …
raffaelecarelle Dec 5, 2025
dac6cf4
remove obsolete regex assertion for stylesheet versioning in RouterTest
raffaelecarelle Dec 5, 2025
f412c52
add unit tests for new header components (`RunStopAllButton`, `RunFai…
raffaelecarelle Dec 5, 2025
70f946e
remove unused sidebar components (`TestItem`, `TestList`, `TestSuite`…
raffaelecarelle Dec 5, 2025
9224ffc
update `FileCoverageDetail` to handle `T_CONSTANT_ENCAPSED_STRING` to…
raffaelecarelle Dec 5, 2025
18a2402
replace `store` with `storeModule` in tests for `resetSidebarTestStat…
raffaelecarelle Dec 5, 2025
865a67e
refactor test handling in `store` to replace spies with direct effect…
raffaelecarelle Dec 5, 2025
6727f78
update `FileCoverageDetail` to refine `T_STRING` token handling logic…
raffaelecarelle Dec 5, 2025
12d937b
remove unused JavaScript build file (`main-BbLX8FxR.js`) to clean up …
raffaelecarelle Dec 5, 2025
fb855bb
remove unused JavaScript build file (`main-BKyrFQMv.js`) to clean up …
raffaelecarelle Dec 5, 2025
6d6ce0b
remove obsolete test cases for event emissions in `MainContent` to st…
raffaelecarelle Dec 5, 2025
4dcab38
remove unused JavaScript build file (`main-CGnKMcxj.js`) to clean up …
raffaelecarelle Dec 5, 2025
46480b0
en comments
raffaelecarelle Dec 9, 2025
82371d1
remove runid concurrent
raffaelecarelle Dec 9, 2025
742e903
fix tests
raffaelecarelle Dec 9, 2025
02e0e65
fix cs
raffaelecarelle Dec 9, 2025
793d03f
remove unused JavaScript build file (`main-BVIOapfr.js`) to clean up …
raffaelecarelle Dec 9, 2025
27d122f
remove unused JavaScript build file (`main-BzLzikyb.js`) to clean up …
raffaelecarelle Dec 9, 2025
5528a7a
fix tests
raffaelecarelle Dec 9, 2025
7496693
wip refactoring-in-components
raffaelecarelle Dec 9, 2025
a74ecae
replace formatNanoseconds
raffaelecarelle Dec 9, 2025
50ffad4
replace fetch tests
raffaelecarelle Dec 9, 2025
2122286
replace fetch tests
raffaelecarelle Dec 9, 2025
6c244fc
replace with composables
raffaelecarelle Dec 9, 2025
585112b
removed console log
raffaelecarelle Dec 9, 2025
cdf2ef0
ref tests
raffaelecarelle Dec 9, 2025
9d7e8e4
update action
raffaelecarelle Dec 9, 2025
a664784
update action
raffaelecarelle Dec 9, 2025
f90349d
Merge remote-tracking branch 'origin/refactoring-in-components' into …
raffaelecarelle Dec 9, 2025
f647afd
drop 18 node version
raffaelecarelle Dec 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ castor.php export-ignore
node_modules export-ignore
public/js/tests export-ignore
tests export-ignore
vendor export-ignore
vendor export-ignore
__tests__ export-ignore
12 changes: 6 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ jobs:
- name: Run PHPStan
run: vendor/bin/phpstan analyse

jest:
name: Jest (Node ${{ matrix.node }})
vitest:
name: Vitest (Node ${{ matrix.node }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node: [ '18', '20', '22', '24' ]
node: [ '20', '22', '24' ]
steps:
- name: Checkout code
uses: actions/checkout@v4
Expand All @@ -53,8 +53,8 @@ jobs:
- name: Install Node dependencies
run: npm ci

- name: Run Jest tests
run: npm test -- --ci --reporters=default
- name: Run Vitest tests
run: npm test

tests:
name: Tests (PHP ${{ matrix.php }}, PHPUnit ${{ matrix.phpunit-version }})
Expand Down Expand Up @@ -92,7 +92,7 @@ jobs:
name: Create Release
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
needs: ["code-quality", "jest", "tests"]
needs: ["code-quality", "vitest", "tests"]
permissions:
contents: write
steps:
Expand Down
63 changes: 63 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,66 @@ composer.lock
.env.*
node_modules
package-lock.json
/public/build/css/styles.css
/public/build/js/components/header/filter/FilterPanelButton.js
/public/build/js/components/header/filter/FilterPanelButton.vue
/public/build/js/components/header/filter/FilterPanelContent.js
/public/build/js/components/header/filter/FilterPanelContent.vue
/public/build/js/components/header/ClearResultsButton.js
/public/build/js/components/header/ClearResultsButton.vue
/public/build/js/components/header/FilterPanel.js
/public/build/js/components/header/FilterPanel.vue
/public/build/js/components/header/HeaderTitle.js
/public/build/js/components/header/HeaderTitle.vue
/public/build/js/components/header/RunFailedButton.js
/public/build/js/components/header/RunFailedButton.vue
/public/build/js/components/header/RunStopAllButton.js
/public/build/js/components/header/RunStopAllButton.vue
/public/build/js/components/sidebar/TestItem.js
/public/build/js/components/sidebar/TestItem.vue
/public/build/js/components/sidebar/TestList.js
/public/build/js/components/sidebar/TestList.vue
/public/build/js/components/sidebar/TestSearchBar.js
/public/build/js/components/sidebar/TestSearchBar.vue
/public/build/js/components/sidebar/TestSuite.js
/public/build/js/components/sidebar/TestSuite.vue
/public/build/js/components/sidebar/TestSuiteHeader.js
/public/build/js/components/sidebar/TestSuiteHeader.vue
/public/build/js/components/CoverageReport.js
/public/build/js/components/CoverageReport.vue
/public/build/js/components/FileCoverageDetail.js
/public/build/js/components/FileCoverageDetail.vue
/public/build/js/components/GroupedTestResults.js
/public/build/js/components/GroupedTestResults.vue
/public/build/js/components/Header.js
/public/build/js/components/Header.vue
/public/build/js/components/IndividualTestResults.js
/public/build/js/components/IndividualTestResults.vue
/public/build/js/components/MainContent.js
/public/build/js/components/MainContent.vue
/public/build/js/components/ResultsSummary.js
/public/build/js/components/ResultsSummary.vue
/public/build/js/components/TabNavigation.js
/public/build/js/components/TabNavigation.vue
/public/build/js/components/TestDetails.js
/public/build/js/components/TestDetails.vue
/public/build/js/components/TestSidebar.js
/public/build/js/components/TestSidebar.vue
/public/build/js/tests/mocks/vue.js
/public/build/js/tests/.gitkeep
/public/build/js/tests/api.test.js
/public/build/js/tests/app.test.js
/public/build/js/tests/store.test.js
/public/build/js/tests/utils.test.js
/public/build/js/tests/websocket.test.js
/public/build/js/api.js
/public/build/js/app.js
/public/build/js/App.vue
/public/build/js/main.js
/public/build/js/store.js
/public/build/js/utils.js
/public/build/js/websocket.js
/public/build/index.html
/public/build/assets/favicon-failure.svg
/public/build/assets/favicon-neutral.svg
/public/build/assets/favicon-success.svg
2 changes: 1 addition & 1 deletion .php-cs-fixer.cache
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"php":"8.2.29","version":"3.90.0:v3.90.0#ad732c2e9299c9743f9c55ae53cc0e7642ab1155","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":{"default":"at_least_single_space"},"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"modifier_keywords":true,"new_with_parentheses":{"anonymous_class":true},"no_blank_lines_after_class_opening":true,"no_extra_blank_lines":{"tokens":["use"]},"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"imports_order":["class","function","const"],"sort_algorithm":"none"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_space_around_construct":{"constructs_followed_by_a_single_space":["abstract","as","case","catch","class","const_import","do","else","elseif","final","finally","for","foreach","function","function_import","if","insteadof","interface","namespace","new","private","protected","public","static","switch","trait","try","use","use_lambda","while"],"constructs_preceded_by_a_single_space":["as","else","elseif","use_lambda"]},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":{"only_dec_inc":true},"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":{"closure_fn_spacing":"one"},"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"after_heredoc":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"octal_notation":true,"clean_namespace":true,"no_unset_cast":true,"assign_null_coalescing_to_coalesce_equal":true,"normalize_index_brace":true,"heredoc_indentation":true,"no_whitespace_before_comma_in_array":{"after_heredoc":true},"trailing_comma_in_multiline":{"after_heredoc":true},"list_syntax":true,"ternary_to_null_coalescing":true,"array_syntax":true,"no_unused_imports":true,"fully_qualified_strict_types":true},"hashes":{"src\/Command\/Router.php":"b2a306bfe90da3e771413b8dea77f416","src\/Command\/ServeCommand.php":"4c45cbcf7c8fe646c68c1ec35d9a4984","src\/Command\/RouterInterface.php":"857c4c498de00c34e01b83ab26b626c2","src\/Coverage\/Coverage.php":"73ff8293849648e1a2100158f75feb39","src\/Util\/Composer.php":"0ece8bbf75bbb97e7d0a583404824538","src\/PHPUnit\/PhpUnitHubExtension.php":"6e23b8845965ecfa033c74078508ffb6","src\/Discoverer\/TestDiscoverer.php":"26db9dbb92284349b05b7320d1250a6f","src\/Parser\/JUnitParser.php":"1a064753b2a0cf80b35c2c6131b3407d","src\/WebSocket\/StatusHandler.php":"e65bbba93f1f1c8bb116140370023aa1","src\/Http\/DecoratedHttpServer.php":"796efd5b1f3413200c4216eacd07baf4","src\/TestRunner\/TestRunner.php":"1ddbf207ccb2a53d2e0674775fa7d074","tests\/Command\/RouterTest.php":"64de0dd90223ca85ef11ab21d9e9f624","tests\/Command\/ServeCommandTest.php":"98e0865c6fd11eb3726bf182e37012e7","tests\/Coverage\/CoverageTest.php":"2f7938f8f414ceb10d8d3b80264fd675","tests\/Discoverer\/TestDiscovererTest.php":"a4354743436349d6c8fdde06e9f3e969","tests\/Parser\/JUnitParserTest.php":"ac9265156b4482b074eef22a06fbd604","tests\/WebSocket\/StatusHandlerTest.php":"532eaf4611becdc32d805efc6411f665","tests\/TestRunner\/TestRunnerTest.php":"f9945b179ecc18a986fd81ad0a4278d8","src\/Util\/ProjectRootResolver.php":"cfc86290611994f3efe9355e84ae4049","src\/PHPUnit\/TestSubscriber.php":"15b9ac0382ea6e9ec64132f357a19076","src\/Discoverer\/PhpUnitCommandExecutor.php":"eda5d8493de9db65695d45770a768efc","tests\/Util\/ProjectRootResolverTest.php":"b77b8208c37629acedd90b4b675f761e","tests\/Util\/ComposerTest.php":"d9c61117c9f424c3bd45724c13a9ef87","tests\/PHPUnit\/TestSubscriberTest.php":"c0ee01fba41bedcd24f36a84baa71191","tests\/Http\/DecoratedHttpServerTest.php":"eca8426406a227d192f817bac2853cb2","src\/Util\/PhpUnitCommandExecutor.php":"84274486bc5836d4dfa6a15ae4711dcc","tests\/Util\/PhpUnitCommandExecutorTest.php":"2f9d8ada0f682222427c4e5689e88d98","tests\/Command\/RouterNewTest.php":"f0925a34a802304469d530db333ea8ef"}}
{"php":"8.2.29","version":"3.91.2:v3.91.2#f171dc216bc8a4192d5a0876a899cce6f9e379d6","indent":" ","lineEnding":"\n","rules":{"binary_operator_spaces":{"default":"at_least_single_space"},"blank_line_after_opening_tag":true,"blank_line_between_import_groups":true,"blank_lines_before_namespace":true,"braces_position":{"allow_single_line_empty_anonymous_classes":true},"class_definition":{"inline_constructor_arguments":false,"space_before_parenthesis":true},"compact_nullable_type_declaration":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"modifier_keywords":true,"new_with_parentheses":{"anonymous_class":true},"no_blank_lines_after_class_opening":true,"no_extra_blank_lines":{"tokens":["use"]},"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"ordered_class_elements":{"order":["use_trait"]},"ordered_imports":{"imports_order":["class","function","const"],"sort_algorithm":"none"},"return_type_declaration":true,"short_scalar_cast":true,"single_import_per_statement":{"group_to_single_imports":false},"single_space_around_construct":{"constructs_followed_by_a_single_space":["abstract","as","case","catch","class","const_import","do","else","elseif","final","finally","for","foreach","function","function_import","if","insteadof","interface","namespace","new","private","protected","public","static","switch","trait","try","use","use_lambda","while"],"constructs_preceded_by_a_single_space":["as","else","elseif","use_lambda"]},"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"unary_operator_spaces":{"only_dec_inc":true},"blank_line_after_namespace":true,"constant_case":true,"control_structure_braces":true,"control_structure_continuation_position":true,"elseif":true,"function_declaration":{"closure_fn_spacing":"one"},"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"after_heredoc":true},"no_break_comment":true,"no_closing_tag":true,"no_multiple_statements_per_line":true,"no_space_around_double_colon":true,"no_spaces_after_function_name":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":{"elements":["property"]},"single_line_after_imports":true,"spaces_inside_parentheses":true,"statement_indentation":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"encoding":true,"full_opening_tag":true,"octal_notation":true,"clean_namespace":true,"no_unset_cast":true,"assign_null_coalescing_to_coalesce_equal":true,"normalize_index_brace":true,"heredoc_indentation":true,"no_whitespace_before_comma_in_array":{"after_heredoc":true},"trailing_comma_in_multiline":{"after_heredoc":true},"list_syntax":true,"ternary_to_null_coalescing":true,"array_syntax":true,"no_unused_imports":true,"fully_qualified_strict_types":true},"hashes":{"src\/Command\/Router.php":"8956c9552a204df48b3825c48a7ade4c","src\/Command\/ServeCommand.php":"4c45cbcf7c8fe646c68c1ec35d9a4984","src\/Command\/RouterInterface.php":"5dd19ba932d4c43c920427003b3c1c3c","src\/Coverage\/Coverage.php":"0cb07471a183377c0b8fc1e6828cd606","src\/Util\/ProjectRootResolver.php":"cfc86290611994f3efe9355e84ae4049","src\/Util\/Composer.php":"0ece8bbf75bbb97e7d0a583404824538","src\/Util\/PhpUnitCommandExecutor.php":"84274486bc5836d4dfa6a15ae4711dcc","src\/PHPUnit\/PhpUnitHubExtension.php":"fe86b1c890e1b827fba07f738a36b24c","src\/Discoverer\/TestDiscoverer.php":"26db9dbb92284349b05b7320d1250a6f","src\/WebSocket\/StatusHandler.php":"e65bbba93f1f1c8bb116140370023aa1","src\/Http\/DecoratedHttpServer.php":"796efd5b1f3413200c4216eacd07baf4","src\/TestRunner\/TestRunner.php":"55f494bb6094e554e31388e3ba8c3431","tests\/Command\/RouterTest.php":"fcbb7a575e2103f020e0aa797dd07e7c","tests\/Command\/ServeCommandTest.php":"98e0865c6fd11eb3726bf182e37012e7","tests\/Coverage\/CoverageTest.php":"2e6a24d482ff721a9deff2f194e0e2bd","tests\/Util\/ProjectRootResolverTest.php":"b77b8208c37629acedd90b4b675f761e","tests\/Util\/ComposerTest.php":"d9c61117c9f424c3bd45724c13a9ef87","tests\/Util\/PhpUnitCommandExecutorTest.php":"2f9d8ada0f682222427c4e5689e88d98","tests\/Discoverer\/TestDiscovererTest.php":"a4354743436349d6c8fdde06e9f3e969","tests\/WebSocket\/StatusHandlerTest.php":"532eaf4611becdc32d805efc6411f665","tests\/Http\/DecoratedHttpServerTest.php":"eca8426406a227d192f817bac2853cb2","tests\/TestRunner\/TestRunnerTest.php":"529b29a3acbe2b17fd709e54cc38c3da"}}
2 changes: 1 addition & 1 deletion .phpunit.result.cache

Large diffs are not rendered by default.

14 changes: 13 additions & 1 deletion public/css/input.css → assets/css/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
padding-bottom: 0;
margin-bottom: 0;
}

input[type="radio"],
input[type="checkbox"] {
-webkit-appearance: none;
Expand Down Expand Up @@ -266,4 +266,16 @@
left: 20px;
animation-delay: 0;
}

[v-cloak] {
display: none;
}

.token-keyword { color: #c586c0; }
.token-string { color: #ce9178; }
.token-comment { color: #6a9955; }
.token-variable { color: #9cdcfe; }
.token-default { color: #d4d4d4; }
.line-covered { background-color: rgba(16, 185, 129, 0.1); }
.line-uncovered { background-color: rgba(248, 113, 113, 0.1); }
}
58 changes: 58 additions & 0 deletions assets/js/App.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<template>
<div id="app" class="flex flex-col h-screen">
<Header></Header>

<!-- Main Container -->
<div class="flex flex-grow overflow-hidden">
<TestSidebar></TestSidebar>

<!-- Resizer -->
<div id="resizer" class="w-1.5 cursor-col-resize bg-gray-700 hover:bg-blue-600 transition-colors duration-200"></div>

<MainContent></MainContent>
</div>
</div>
</template>

<script setup>
import { onMounted, watch } from 'vue';
import { useStore } from './store.js';
import { WebSocketManager } from './websocket.js';
import { updateFavicon } from './utils.js';
import { useResizer } from './composables/useResizer.js';

import Header from './components/Header.vue';
import TestSidebar from './components/TestSidebar.vue';
import MainContent from './components/MainContent.vue';

const store = useStore();
let wsManager = null;

useResizer('resizer', 'test-sidebar');

onMounted(async () => {
try {
// Connect WebSocket
const wsHost = window.WS_HOST || '127.0.0.1';
const wsPort = window.WS_PORT || '8080';
wsManager = new WebSocketManager(`ws://${wsHost}:${wsPort}/ws/status`, store, {
fetchCoverageReport: store.fetchCoverageReport,
});
await wsManager.connect();

// Update favicon
updateFavicon('neutral');
} catch (error) {
console.error('Failed to initialize app:', error);
}
});

watch(() => [
store.state.options,
store.state.selectedSuites,
store.state.selectedGroups,
store.state.coverage
], () => {
store.saveState();
}, { deep: true });
</script>
Loading