Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,29 @@ jobs:
- name: Clean build
run: ./gradlew clean build --no-daemon --stacktrace

- name: Verify guest PvP UCI regression test ran in build
run: |
python - <<'PY'
import glob
import xml.etree.ElementTree as ET

test_name = "guestPlayersCanPlayFullPvPGameFromUciSample()"
files = glob.glob("webapp-service-layer/build/test-results/test/TEST-*.xml")

for path in files:
root = ET.parse(path).getroot()
for testcase in root.findall(".//testcase"):
if testcase.attrib.get("name") == test_name:
if testcase.find("failure") is not None:
raise SystemExit(f"{test_name} failed in {path}")
if testcase.find("skipped") is not None:
raise SystemExit(f"{test_name} was skipped in {path}")
print(f"Verified: {test_name} passed in {path}")
raise SystemExit(0)

raise SystemExit(f"Could not find {test_name} in JUnit reports")
PY

- name: Publish test report
if: always()
uses: mikepenz/action-junit-report@v6
Expand Down
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,38 @@ Under `webapp/src/main/resources/public/js` there's usually a sub-folder for eac
Complex app like PvP is usually organized with a **page** which updates the GUI, a **controller** which connects to
WebSockets and/or calls REST endpoints, sometimes a DTO file and/or a separate REST client file.

### Page tests (E2E)

If you want browser-level page tests, prefer **Playwright** over Selenium for this project:

- easier setup and test authoring
- built-in waiting/retry behavior
- isolated browser contexts for multi-user scenarios

For PvP tests, run each player in a different private context so each one gets a different guest ID:

```javascript
import { test, chromium } from '@playwright/test';

test('pvp with 2 guest sessions', async () => {
const browser = await chromium.launch(); // `async ({ browser })` can be used with Playwright fixtures too
const player1Context = await browser.newContext();
const player2Context = await browser.newContext();

const player1Page = await player1Context.newPage();
const player2Page = await player2Context.newPage();

await player1Page.goto('http://localhost:8080');
await player2Page.goto('http://localhost:8080');

await player1Context.close();
await player2Context.close();
await browser.close();
});
```

Use two separate browsers only if you specifically need different browser engines (for example Chromium vs Firefox).

## JavaScript Libraries

Some widgets from the [https://elephantchess.io](https://elephantchess.io) front-end are available as JavaScript
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,53 @@ class PlayerVsPlayerGameServiceTest : ServiceTest() {
}

@Test
fun `guests users not allowed to join games with option allowGuests == false`() = runTest {
fun guestPlayersCanPlayFullPvPGameFromUciSample() = runTest {
val request = CreateGameRequest(
inviterColor = RED,
isRated = false,
timeControlBase = 30.minutes.inWholeSeconds.toInt(),
timeControlIncrement = null,
timeControlMode = TimeControlMode.GAME_TIME,
allowGuests = true,
alwaysVisibleInLobby = false,
privateInvite = false
)

val createResponse = pvpGameService.createGame(guestId1, request)
assertEquals(CREATED, createResponse.eventType)
assertEquals(RED, createResponse.color)

pvpGameService.joinGame(guestId2, JoinGameRequest(createResponse.gameId))
assertEquals(1, countGameByStatus(JOINED))
assertEquals(0, countGameByStatus(CREATED))

val gameMoves = assertNotNull(
gameMovesCache.listAll().firstOrNull { it.endsInCheckmate() },
"Expected at least one Xiangqi game in uci.txt that ends in checkmate"
)

gameMoves.uciMoves.dropLast(1).forEachIndexed { i, move ->
val result = pvpGameService.playMove(
userId = userIdToPlay(createResponse.gameId),
request = PlayMoveRequest(createResponse.gameId, move)
)

assertEquals(i + 1, result.updatedIndex)
assertNull(result.gameEventType)
assertNull(result.ratingUpdate)
}

val lastMoveResult = pvpGameService.playMove(
userId = userIdToPlay(createResponse.gameId),
request = PlayMoveRequest(createResponse.gameId, gameMoves.uciMoves.last())
)

assertEquals(CHECKMATED, lastMoveResult.gameEventType)
assertNull(lastMoveResult.ratingUpdate)
}

@Test
fun `guest users not allowed to join games with option allowGuests == false`() = runTest {
val request1 = CreateGameRequest(
inviterColor = RED,
isRated = true,
Expand Down