diff --git a/ui-tests-starter/tst-243+/software/aws/toolkits/jetbrains/uitests/docTests/UpdateCodeChangesFlowTest.kt b/ui-tests-starter/tst-243+/software/aws/toolkits/jetbrains/uitests/docTests/UpdateCodeChangesFlowTest.kt new file mode 100644 index 00000000000..2e6447a75f6 --- /dev/null +++ b/ui-tests-starter/tst-243+/software/aws/toolkits/jetbrains/uitests/docTests/UpdateCodeChangesFlowTest.kt @@ -0,0 +1,178 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.uitests.docTests + +import com.intellij.driver.sdk.waitForProjectOpen +import com.intellij.ide.starter.ci.CIServer +import com.intellij.ide.starter.config.ConfigurationStorage +import com.intellij.ide.starter.di.di +import com.intellij.ide.starter.driver.engine.runIdeWithDriver +import com.intellij.ide.starter.ide.IdeProductProvider +import com.intellij.ide.starter.junit5.hyphenateWithClass +import com.intellij.ide.starter.models.TestCase +import com.intellij.ide.starter.project.LocalProjectInfo +import com.intellij.ide.starter.runner.CurrentTestMethod +import com.intellij.ide.starter.runner.Starter +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.kodein.di.DI +import org.kodein.di.bindSingleton +import software.aws.toolkits.jetbrains.uitests.TestCIServer +import software.aws.toolkits.jetbrains.uitests.clearAwsXmlFile +import software.aws.toolkits.jetbrains.uitests.docTests.scripts.updateReadmeAcceptChangesTestScript +import software.aws.toolkits.jetbrains.uitests.docTests.scripts.updateReadmeConfirmOptionsTestScript +import software.aws.toolkits.jetbrains.uitests.docTests.scripts.updateReadmeMakeChangesTestScript +import software.aws.toolkits.jetbrains.uitests.executePuppeteerScript +import software.aws.toolkits.jetbrains.uitests.setupTestEnvironment +import software.aws.toolkits.jetbrains.uitests.useExistingConnectionForTest +import java.io.File +import java.net.URI +import java.nio.file.Path +import java.nio.file.Paths + +class UpdateCodeChangesFlowTest { + + init { + di = DI { + extend(di) + bindSingleton(overrides = true) { TestCIServer } + val defaults = ConfigurationStorage.instance().defaults.toMutableMap().apply { + put("LOG_ENVIRONMENT_VARIABLES", (!System.getenv("CI").toBoolean()).toString()) + } + + bindSingleton(overrides = true) { + ConfigurationStorage(this, defaults) + } + } + } + + @BeforeEach + fun setUp() { + // Setup test environment + setupTestEnvironment() + } + + @Test + fun `User is prompted to Confirm Selected Folder`() { + val testCase = TestCase( + IdeProductProvider.IC, + LocalProjectInfo( + Paths.get("tstData", "Hello") + ) + ).useRelease(System.getProperty("org.gradle.project.ideProfileName")) + + // inject connection + useExistingConnectionForTest() + + Starter.newContext(CurrentTestMethod.hyphenateWithClass(), testCase).apply { + System.getProperty("ui.test.plugins").split(File.pathSeparator).forEach { path -> + pluginConfigurator.installPluginFromPath( + Path.of(path) + ) + } + + copyExistingConfig(Paths.get("tstData", "configAmazonQTests")) + updateGeneralSettings() + }.runIdeWithDriver() + .useDriverAndCloseIde { + waitForProjectOpen() + // required wait time for the system to be fully ready + Thread.sleep(30000) + + val result = executePuppeteerScript(updateReadmeConfirmOptionsTestScript) + + assertTrue(result.contains("found buttons looks for in the text")) + assertFalse(result.contains("buttons not found")) + } + } + + @Test + fun `Make changes button brings you to UPDATE w specific changes flow`() { + val testCase = TestCase( + IdeProductProvider.IC, + LocalProjectInfo( + Paths.get("tstData", "Hello") + ) + ).useRelease(System.getProperty("org.gradle.project.ideProfileName")) + + // inject connection + useExistingConnectionForTest() + + Starter.newContext(CurrentTestMethod.hyphenateWithClass(), testCase).apply { + System.getProperty("ui.test.plugins").split(File.pathSeparator).forEach { path -> + pluginConfigurator.installPluginFromPath( + Path.of(path) + ) + } + + copyExistingConfig(Paths.get("tstData", "configAmazonQTests")) + updateGeneralSettings() + }.runIdeWithDriver() + .useDriverAndCloseIde { + waitForProjectOpen() + waitForProjectOpen() + // required wait time for the system to be fully ready + Thread.sleep(30000) + + val result = executePuppeteerScript(updateReadmeMakeChangesTestScript) + assertFalse(result.contains("Could not find the placeholder text")) + assertTrue(result.contains("Found documentation change input text")) + } + } + + @Test + fun `Update README with latest changes flow`() { + val testCase = TestCase( + IdeProductProvider.IC, + LocalProjectInfo( + Paths.get("tstData", "Hello") + ) + ).useRelease(System.getProperty("org.gradle.project.ideProfileName")) + + // inject connection + useExistingConnectionForTest() + + Starter.newContext(CurrentTestMethod.hyphenateWithClass(), testCase).apply { + System.getProperty("ui.test.plugins").split(File.pathSeparator).forEach { path -> + pluginConfigurator.installPluginFromPath( + Path.of(path) + ) + } + + copyExistingConfig(Paths.get("tstData", "configAmazonQTests")) + updateGeneralSettings() + }.runIdeWithDriver() + .useDriverAndCloseIde { + waitForProjectOpen() + // required wait time for the system to be fully ready + Thread.sleep(30000) + + val result = executePuppeteerScript(updateReadmeAcceptChangesTestScript) + + val readmePath = getReadmePath(delete = true) + val readmeFile = File(readmePath) + val content = readmeFile.readText() + assertTrue(content.contains("tancode")) + } + } + + private fun getReadmePath(delete: Boolean = false): URI { + val readmePath = Paths.get("tstData", "Hello", "README.md").toUri() + if (delete) { + File(readmePath).takeIf { it.exists() }?.delete() + } + return readmePath + } + + companion object { + @JvmStatic + @AfterAll + fun clearAwsXml() { + clearAwsXmlFile() + } + } +} diff --git a/ui-tests-starter/tst-243+/software/aws/toolkits/jetbrains/uitests/docTests/scripts/UpdateReadMeAcceptChanges.kt b/ui-tests-starter/tst-243+/software/aws/toolkits/jetbrains/uitests/docTests/scripts/UpdateReadMeAcceptChanges.kt new file mode 100644 index 00000000000..145ba1921a5 --- /dev/null +++ b/ui-tests-starter/tst-243+/software/aws/toolkits/jetbrains/uitests/docTests/scripts/UpdateReadMeAcceptChanges.kt @@ -0,0 +1,58 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.uitests.docTests.scripts + +// language=TS +val updateReadmeAcceptChangesScript = """ + + const puppeteer = require('puppeteer'); + + async function testNavigation() { + const browser = await puppeteer.connect({ + browserURL: 'http://localhost:9222' + }) + + try { + + const pages = await browser.pages() + //console.log(pages) + for(const page of pages) { + const contents = await page.evaluate(el => el.innerHTML, await page.${'$'}(':root')); + //console.log(contents) + const element = await page.${'$'}('.mynah-chat-prompt-input') + if(element) { + console.log('found') + + await page.type('.mynah-chat-prompt-input', '/doc') + await page.keyboard.press('Enter') + + console.log('entered /doc') + console.log('found commands') + + await findAndClickButton(page, 'Update an existing README', true, 10000) + console.log('clicked update readme') + await findAndClickButton(page, 'Update README to reflect code', true, 10000) + console.log('clicked update readme to reflect code') + await findAndClickButton(page, 'Yes', true, 10000) + console.log('clicked yes') + + await new Promise(resolve => setTimeout(resolve, 90000)); + + await findAndClickButton(page, 'Accept', true, 10000) + console.log('clicked Accept') + + } + } + + + } finally { + await browser.close(); + } + } + + testNavigation().catch(console.error); + +""".trimIndent() + +val updateReadmeAcceptChangesTestScript = updateReadmeAcceptChangesScript.plus(findAndClickButton) diff --git a/ui-tests-starter/tst-243+/software/aws/toolkits/jetbrains/uitests/docTests/scripts/UpdateReadMeConfirmOptions.kt b/ui-tests-starter/tst-243+/software/aws/toolkits/jetbrains/uitests/docTests/scripts/UpdateReadMeConfirmOptions.kt new file mode 100644 index 00000000000..4a63922f952 --- /dev/null +++ b/ui-tests-starter/tst-243+/software/aws/toolkits/jetbrains/uitests/docTests/scripts/UpdateReadMeConfirmOptions.kt @@ -0,0 +1,58 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.uitests.docTests.scripts + +// language=TS +val updateReadmeConfirmOptionsScript = """ + + const puppeteer = require('puppeteer'); + + async function testNavigation() { + const browser = await puppeteer.connect({ + browserURL: 'http://localhost:9222' + }) + + try { + + const pages = await browser.pages() + //console.log(pages) + for(const page of pages) { + const contents = await page.evaluate(el => el.innerHTML, await page.${'$'}(':root')); + //console.log(contents) + const element = await page.${'$'}('.mynah-chat-prompt-input') + if(element) { + console.log('found') + + await page.type('.mynah-chat-prompt-input', '/doc') + await page.keyboard.press('Enter') + + console.log('entered /doc') + console.log('found commands') + + await findAndClickButton(page, 'Update an existing README', true, 10000) + console.log('clicked update readme') + await findAndClickButton(page, 'Update README to reflect code', true, 10000) + + const yesButton = await findAndClickButton(page, 'Yes', false, 10000) + const changeFolderButton = await findAndClickButton(page, 'Change folder', false, 10000) + const cancelButton = await findAndClickButton(page, 'Cancel', false, 10000) + + if (!yesButton || !changeFolderButton || !cancelButton) { + console.log('buttons not found') + } else { + console.log('found buttons looks for in the text') + } + } + } + + } finally { + await browser.close(); + } + } + + testNavigation().catch(console.error); + +""".trimIndent() + +val updateReadmeConfirmOptionsTestScript = updateReadmeConfirmOptionsScript.plus(findAndClickButton) diff --git a/ui-tests-starter/tst-243+/software/aws/toolkits/jetbrains/uitests/docTests/scripts/UpdateReadMeMakeChanges.kt b/ui-tests-starter/tst-243+/software/aws/toolkits/jetbrains/uitests/docTests/scripts/UpdateReadMeMakeChanges.kt new file mode 100644 index 00000000000..78685c15e20 --- /dev/null +++ b/ui-tests-starter/tst-243+/software/aws/toolkits/jetbrains/uitests/docTests/scripts/UpdateReadMeMakeChanges.kt @@ -0,0 +1,63 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.uitests.docTests.scripts + +// language=TS +val updateReadmeMakeChangesScript = """ + + const puppeteer = require('puppeteer'); + + async function testNavigation() { + const browser = await puppeteer.connect({ + browserURL: 'http://localhost:9222' + }) + + try { + + const pages = await browser.pages() + //console.log(pages) + for(const page of pages) { + const contents = await page.evaluate(el => el.innerHTML, await page.${'$'}(':root')); + //console.log(contents) + const element = await page.${'$'}('.mynah-chat-prompt-input') + if(element) { + console.log('found') + + await page.type('.mynah-chat-prompt-input', '/doc') + await page.keyboard.press('Enter') + + console.log('entered /doc') + console.log('found commands') + + await findAndClickButton(page, 'Update an existing README', true, 10000) + console.log('clicked update readme') + await findAndClickButton(page, 'Update README to reflect code', true, 10000) + console.log('clicked update readme to reflect code') + await findAndClickButton(page, 'Yes', true, 10000) + console.log('clicked yes') + + await new Promise(resolve => setTimeout(resolve, 90000)); + + await findAndClickButton(page, 'Make changes', true, 10000) + console.log('clicked make changes') + + const makeChangeText = await page.${'$'}('[placeholder="Describe documentation changes"]') + if (!makeChangeText) { + throw new Error('Could not find the placeholder text'); + } else { + console.log("Found documentation change input text") + } + } + } + + + } finally { + await browser.close(); + } + } + + testNavigation().catch(console.error); +""".trimIndent() + +val updateReadmeMakeChangesTestScript = updateReadmeMakeChangesScript.plus(findAndClickButton) diff --git a/ui-tests-starter/tst-243+/software/aws/toolkits/jetbrains/uitests/docTests/scripts/Utils.kt b/ui-tests-starter/tst-243+/software/aws/toolkits/jetbrains/uitests/docTests/scripts/Utils.kt new file mode 100644 index 00000000000..45da366cabe --- /dev/null +++ b/ui-tests-starter/tst-243+/software/aws/toolkits/jetbrains/uitests/docTests/scripts/Utils.kt @@ -0,0 +1,64 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.uitests.docTests.scripts + +// language=TS +val findAndClickButton = """ + async function findAndClickButton(page, buttonText, clickButton = false, timeout = 5000) { + try { + // Wait for any matching buttons to be present + await page.waitForSelector('button.mynah-button', { + visible: true, + timeout + }); + + // Find and verify the specific button + const buttonHandle = await page.evaluateHandle((text) => { + const buttons = Array.from(document.querySelectorAll('button.mynah-button')); + return buttons.find(button => { + const label = button.querySelector('.mynah-button-label'); + return label && label.textContent.trim() === text; + }); + }, buttonText); + + // Check if button was found + const button = buttonHandle.asElement(); + if (!button) { + console.log(buttonText) + throw new Error(`Button with text not found`); + } + + // Verify button is visible and enabled + const isVisible = await page.evaluate(el => { + const style = window.getComputedStyle(el); + return style.display !== 'none' && + style.visibility !== 'hidden' && + style.opacity !== '0'; + }, button); + + if (!isVisible) { + console.log(buttonText) + throw new Error(`Button with text is not visible`); + } + + if (clickButton) { + // Click the button + await button.click(); + + // Optional wait after click + await new Promise(resolve => setTimeout(resolve, 1000)); + + console.log(`Successfully clicked button with text`); + console.log(buttonText) + } else { + return button; + } + + + } catch (error) { + console.error(`Error interacting with button:`, buttonText, error); + throw error; + } + } +""".trimIndent() diff --git a/ui-tests-starter/tstData/QDocTestRepository/README.md b/ui-tests-starter/tstData/QDocTestRepository/README.md new file mode 100644 index 00000000000..76fd0231309 --- /dev/null +++ b/ui-tests-starter/tstData/QDocTestRepository/README.md @@ -0,0 +1,99 @@ +# Java Minesweeper Game - Classic Mine-Finding Strategy Game Implementation + +This project is a complete implementation of the classic Minesweeper game in Java using Swing for the graphical user interface. The game provides an interactive 16x16 grid with 40 mines where players can reveal cells, mark potential mine locations, and experience the classic Minesweeper gameplay with a clean, modern interface. + +The implementation features a robust game engine that handles mine placement, neighbor calculation, and recursive empty cell revelation. It includes a status bar that displays the number of remaining mines and game state (won/lost), mouse interaction support for both left-click (reveal) and right-click (mark) actions, and automatic game state management. The game follows the traditional Minesweeper rules where players must reveal all non-mine cells while avoiding mines to win. + +## Repository Structure +``` +ui-tests-starter/tstData/Hello/ +├── catalog-info.yaml # Backstage component definition file +└── src/com/zetcode/ # Main source code directory + ├── Board.java # Core game logic and UI rendering + └── Minesweeper.java # Main application entry point and window setup +``` + +## Usage Instructions +### Prerequisites +- Java Development Kit (JDK) 11 or higher +- Java Runtime Environment (JRE) +- Java Swing library (included in JDK) + +### Installation +1. Clone the repository: +```bash +git clone +cd ui-tests-starter/tstData/Hello +``` + +2. Compile the Java files: +```bash +javac src/com/zetcode/*.java +``` + +### Quick Start +1. Run the compiled game: +```bash +java -cp src com.zetcode.Minesweeper +``` + +2. Play the game: +- Left-click to reveal a cell +- Right-click to mark/unmark a potential mine +- The status bar shows remaining mines or game status +- Reveal all non-mine cells to win + +### More Detailed Examples +#### Game Controls +```java +// Left-click to reveal a cell +mousePressed(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON1) { + // Reveals the cell + } +} + +// Right-click to mark a cell +mousePressed(MouseEvent e) { + if (e.getButton() == MouseEvent.BUTTON3) { + // Marks/unmarks the cell as a potential mine + } +} +``` + +### Troubleshooting +#### Common Issues +1. Missing Images Error +- Problem: Game fails to load cell images +- Solution: Ensure the image resources are in the correct path: `src/resources/` +- Required files: 0.png through 12.png for different cell states + +2. Game Window Not Appearing +- Check if Java Swing is properly initialized +- Verify the main class is being executed correctly +- Ensure no other processes are blocking the window creation + +#### Performance Optimization +- The game is optimized for a 16x16 grid with 40 mines +- Rendering occurs only when necessary through selective repainting +- Cell revelation uses efficient recursive algorithms for empty cells + +## Data Flow +The game processes user input through mouse events, updates the game state matrix, and renders the updated board state to the screen. + +```ascii +[User Input] -> [Mouse Event Handler] -> [Game State Update] -> [Board Rendering] + ^ | | + | | | + +----------------------------------------+----------------------+ + Game Loop Feedback +``` + +Key Component Interactions: +1. Minesweeper class initializes the main window and status bar +2. Board class manages the game state and handles user input +3. Mouse events trigger cell revelation or marking +4. Game state updates propagate through the board matrix +5. Status bar displays game progress and results +6. Graphics system renders the updated board state +7. Cell state changes trigger recursive updates for empty cells diff --git a/ui-tests-starter/tstData/QDocTestRepository/catalog-info.yaml b/ui-tests-starter/tstData/QDocTestRepository/catalog-info.yaml new file mode 100644 index 00000000000..2367eb806d4 --- /dev/null +++ b/ui-tests-starter/tstData/QDocTestRepository/catalog-info.yaml @@ -0,0 +1,13 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: jbe-mines + description: It's a java minesweeper thing + labels: + example.com/custom: custom_label_value + tags: + - java +spec: + type: service + lifecycle: experimental + owner: mines-relations-team diff --git a/ui-tests-starter/tstData/QDocTestRepository/minesweeper.png b/ui-tests-starter/tstData/QDocTestRepository/minesweeper.png new file mode 100644 index 00000000000..452aaedfaa6 Binary files /dev/null and b/ui-tests-starter/tstData/QDocTestRepository/minesweeper.png differ diff --git a/ui-tests-starter/tstData/QDocTestRepository/src/com/zetcode/Board.java b/ui-tests-starter/tstData/QDocTestRepository/src/com/zetcode/Board.java new file mode 100644 index 00000000000..23c7876c13e --- /dev/null +++ b/ui-tests-starter/tstData/QDocTestRepository/src/com/zetcode/Board.java @@ -0,0 +1,380 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.zetcode; + +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.Random; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JPanel; + +public class Board extends JPanel { + + private final int NUM_IMAGES = 13; + private final int CELL_SIZE = 15; + + private final int COVER_FOR_CELL = 10; + private final int MARK_FOR_CELL = 10; + private final int EMPTY_CELL = 0; + private final int MINE_CELL = 9; + private final int COVERED_MINE_CELL = MINE_CELL + COVER_FOR_CELL; + private final int MARKED_MINE_CELL = COVERED_MINE_CELL + MARK_FOR_CELL; + + private final int DRAW_MINE = 9; + private final int DRAW_COVER = 10; + private final int DRAW_MARK = 11; + private final int DRAW_WRONG_MARK = 12; + + private final int N_MINES = 40; + private final int N_ROWS = 16; + private final int N_COLS = 16; + + private final int BOARD_WIDTH = N_COLS * CELL_SIZE + 1; + private final int BOARD_HEIGHT = N_ROWS * CELL_SIZE + 1; + + private int[] field; + private boolean inGame; + private int minesLeft; + private Image[] img; + + private int allCells; + private final JLabel statusbar; + + public Board(JLabel statusbar) { + + this.statusbar = statusbar; + initBoard(); + } + + private void initBoard() { + + setPreferredSize(new Dimension(BOARD_WIDTH, BOARD_HEIGHT)); + + img = new Image[NUM_IMAGES]; + + for (int i = 0; i < NUM_IMAGES; i++) { + + var path = "src/resources/" + i + ".png"; + img[i] = (new ImageIcon(path)).getImage(); + } + + addMouseListener(new MinesAdapter()); + newGame(); + } + + private void newGame() { + + int cell; + + var random = new Random(); + inGame = true; + minesLeft = N_MINES; + + allCells = N_ROWS * N_COLS; + field = new int[allCells]; + + for (int i = 0; i < allCells; i++) { + + field[i] = COVER_FOR_CELL; + } + + statusbar.setText(Integer.toString(minesLeft)); + + int i = 0; + + while (i < N_MINES) { + + int position = (int) (allCells * random.nextDouble()); + + if ((position < allCells) + && (field[position] != COVERED_MINE_CELL)) { + + int current_col = position % N_COLS; + field[position] = COVERED_MINE_CELL; + i++; + + if (current_col > 0) { + cell = position - 1 - N_COLS; + if (cell >= 0) { + if (field[cell] != COVERED_MINE_CELL) { + field[cell] += 1; + } + } + cell = position - 1; + if (cell >= 0) { + if (field[cell] != COVERED_MINE_CELL) { + field[cell] += 1; + } + } + + cell = position + N_COLS - 1; + if (cell < allCells) { + if (field[cell] != COVERED_MINE_CELL) { + field[cell] += 1; + } + } + } + + cell = position - N_COLS; + if (cell >= 0) { + if (field[cell] != COVERED_MINE_CELL) { + field[cell] += 1; + } + } + + cell = position + N_COLS; + if (cell < allCells) { + if (field[cell] != COVERED_MINE_CELL) { + field[cell] += 1; + } + } + + if (current_col < (N_COLS - 1)) { + cell = position - N_COLS + 1; + if (cell >= 0) { + if (field[cell] != COVERED_MINE_CELL) { + field[cell] += 1; + } + } + cell = position + N_COLS + 1; + if (cell < allCells) { + if (field[cell] != COVERED_MINE_CELL) { + field[cell] += 1; + } + } + cell = position + 1; + if (cell < allCells) { + if (field[cell] != COVERED_MINE_CELL) { + field[cell] += 1; + } + } + } + } + } + } + + private void find_empty_cells(int j) { + + int current_col = j % N_COLS; + int cell; + + if (current_col > 0) { + cell = j - N_COLS - 1; + if (cell >= 0) { + if (field[cell] > MINE_CELL) { + field[cell] -= COVER_FOR_CELL; + if (field[cell] == EMPTY_CELL) { + find_empty_cells(cell); + } + } + } + + cell = j - 1; + if (cell >= 0) { + if (field[cell] > MINE_CELL) { + field[cell] -= COVER_FOR_CELL; + if (field[cell] == EMPTY_CELL) { + find_empty_cells(cell); + } + } + } + + cell = j + N_COLS - 1; + if (cell < allCells) { + if (field[cell] > MINE_CELL) { + field[cell] -= COVER_FOR_CELL; + if (field[cell] == EMPTY_CELL) { + find_empty_cells(cell); + } + } + } + } + + cell = j - N_COLS; + if (cell >= 0) { + if (field[cell] > MINE_CELL) { + field[cell] -= COVER_FOR_CELL; + if (field[cell] == EMPTY_CELL) { + find_empty_cells(cell); + } + } + } + + cell = j + N_COLS; + if (cell < allCells) { + if (field[cell] > MINE_CELL) { + field[cell] -= COVER_FOR_CELL; + if (field[cell] == EMPTY_CELL) { + find_empty_cells(cell); + } + } + } + + if (current_col < (N_COLS - 1)) { + cell = j - N_COLS + 1; + if (cell >= 0) { + if (field[cell] > MINE_CELL) { + field[cell] -= COVER_FOR_CELL; + if (field[cell] == EMPTY_CELL) { + find_empty_cells(cell); + } + } + } + + cell = j + N_COLS + 1; + if (cell < allCells) { + if (field[cell] > MINE_CELL) { + field[cell] -= COVER_FOR_CELL; + if (field[cell] == EMPTY_CELL) { + find_empty_cells(cell); + } + } + } + + cell = j + 1; + if (cell < allCells) { + if (field[cell] > MINE_CELL) { + field[cell] -= COVER_FOR_CELL; + if (field[cell] == EMPTY_CELL) { + find_empty_cells(cell); + } + } + } + } + + } + + @Override + public void paintComponent(Graphics g) { + + int uncover = 0; + + for (int i = 0; i < N_ROWS; i++) { + + for (int j = 0; j < N_COLS; j++) { + + int cell = field[(i * N_COLS) + j]; + + if (inGame && cell == MINE_CELL) { + + inGame = false; + } + + if (!inGame) { + + if (cell == COVERED_MINE_CELL) { + cell = DRAW_MINE; + } else if (cell == MARKED_MINE_CELL) { + cell = DRAW_MARK; + } else if (cell > COVERED_MINE_CELL) { + cell = DRAW_WRONG_MARK; + } else if (cell > MINE_CELL) { + cell = DRAW_COVER; + } + + } else { + + if (cell > COVERED_MINE_CELL) { + cell = DRAW_MARK; + } else if (cell > MINE_CELL) { + cell = DRAW_COVER; + uncover++; + } + } + + g.drawImage(img[cell], (j * CELL_SIZE), + (i * CELL_SIZE), this); + } + } + + if (uncover == 0 && inGame) { + + inGame = false; + statusbar.setText("Game won"); + + } else if (!inGame) { + statusbar.setText("Game lost"); + } + } + + private class MinesAdapter extends MouseAdapter { + + @Override + public void mousePressed(MouseEvent e) { + + int x = e.getX(); + int y = e.getY(); + + int cCol = x / CELL_SIZE; + int cRow = y / CELL_SIZE; + + boolean doRepaint = false; + + if (!inGame) { + + newGame(); + repaint(); + } + + if ((x < N_COLS * CELL_SIZE) && (y < N_ROWS * CELL_SIZE)) { + + if (e.getButton() == MouseEvent.BUTTON3) { + + if (field[(cRow * N_COLS) + cCol] > MINE_CELL) { + + doRepaint = true; + + if (field[(cRow * N_COLS) + cCol] <= COVERED_MINE_CELL) { + + if (minesLeft > 0) { + field[(cRow * N_COLS) + cCol] += MARK_FOR_CELL; + minesLeft--; + String msg = Integer.toString(minesLeft); + statusbar.setText(msg); + } else { + statusbar.setText("No marks left"); + } + } else { + + field[(cRow * N_COLS) + cCol] -= MARK_FOR_CELL; + minesLeft++; + String msg = Integer.toString(minesLeft); + statusbar.setText(msg); + } + } + + } else { + + if (field[(cRow * N_COLS) + cCol] > COVERED_MINE_CELL) { + + return; + } + + if ((field[(cRow * N_COLS) + cCol] > MINE_CELL) + && (field[(cRow * N_COLS) + cCol] < MARKED_MINE_CELL)) { + + field[(cRow * N_COLS) + cCol] -= COVER_FOR_CELL; + doRepaint = true; + + if (field[(cRow * N_COLS) + cCol] == MINE_CELL) { + inGame = false; + } + + if (field[(cRow * N_COLS) + cCol] == EMPTY_CELL) { + find_empty_cells((cRow * N_COLS) + cCol); + } + } + } + + if (doRepaint) { + repaint(); + } + } + } + } +} diff --git a/ui-tests-starter/tstData/QDocTestRepository/src/com/zetcode/Minesweeper.java b/ui-tests-starter/tstData/QDocTestRepository/src/com/zetcode/Minesweeper.java new file mode 100644 index 00000000000..69aaaf0b80c --- /dev/null +++ b/ui-tests-starter/tstData/QDocTestRepository/src/com/zetcode/Minesweeper.java @@ -0,0 +1,50 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package com.zetcode; + +import java.awt.BorderLayout; +import java.awt.EventQueue; +import javax.swing.JFrame; +import javax.swing.JLabel; + +/** + * Java Minesweeper Game + * + * Author: Jan Bodnar + * Website: http://zetcode.com + */ + +public class Minesweeper extends JFrame { + + private JLabel statusbar; + + public Minesweeper() { + + initUI(); + } + + private void initUI() { + + statusbar = new JLabel(""); + add(statusbar, BorderLayout.SOUTH); + + add(new Board(statusbar)); + + setResizable(false); + pack(); + + setTitle("Minesweeper"); + setLocationRelativeTo(null); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + } + + public static void main(String[] args) { + + EventQueue.invokeLater(() -> { + + var ex = new Minesweeper(); + ex.setVisible(true); + }); + } +} diff --git a/ui-tests-starter/tstData/QDocTestRepository/src/resources/0.png b/ui-tests-starter/tstData/QDocTestRepository/src/resources/0.png new file mode 100644 index 00000000000..afebd3af9a4 Binary files /dev/null and b/ui-tests-starter/tstData/QDocTestRepository/src/resources/0.png differ diff --git a/ui-tests-starter/tstData/QDocTestRepository/src/resources/1.png b/ui-tests-starter/tstData/QDocTestRepository/src/resources/1.png new file mode 100644 index 00000000000..9da15a3ca2a Binary files /dev/null and b/ui-tests-starter/tstData/QDocTestRepository/src/resources/1.png differ diff --git a/ui-tests-starter/tstData/QDocTestRepository/src/resources/10.png b/ui-tests-starter/tstData/QDocTestRepository/src/resources/10.png new file mode 100644 index 00000000000..83ded9d8b9c Binary files /dev/null and b/ui-tests-starter/tstData/QDocTestRepository/src/resources/10.png differ diff --git a/ui-tests-starter/tstData/QDocTestRepository/src/resources/11.png b/ui-tests-starter/tstData/QDocTestRepository/src/resources/11.png new file mode 100644 index 00000000000..84ab24fcfdc Binary files /dev/null and b/ui-tests-starter/tstData/QDocTestRepository/src/resources/11.png differ diff --git a/ui-tests-starter/tstData/QDocTestRepository/src/resources/12.png b/ui-tests-starter/tstData/QDocTestRepository/src/resources/12.png new file mode 100644 index 00000000000..949b27610f3 Binary files /dev/null and b/ui-tests-starter/tstData/QDocTestRepository/src/resources/12.png differ diff --git a/ui-tests-starter/tstData/QDocTestRepository/src/resources/2.png b/ui-tests-starter/tstData/QDocTestRepository/src/resources/2.png new file mode 100644 index 00000000000..ef1fd09175b Binary files /dev/null and b/ui-tests-starter/tstData/QDocTestRepository/src/resources/2.png differ diff --git a/ui-tests-starter/tstData/QDocTestRepository/src/resources/3.png b/ui-tests-starter/tstData/QDocTestRepository/src/resources/3.png new file mode 100644 index 00000000000..4a70f7d5e72 Binary files /dev/null and b/ui-tests-starter/tstData/QDocTestRepository/src/resources/3.png differ diff --git a/ui-tests-starter/tstData/QDocTestRepository/src/resources/4.png b/ui-tests-starter/tstData/QDocTestRepository/src/resources/4.png new file mode 100644 index 00000000000..f29bcaf8c4c Binary files /dev/null and b/ui-tests-starter/tstData/QDocTestRepository/src/resources/4.png differ diff --git a/ui-tests-starter/tstData/QDocTestRepository/src/resources/5.png b/ui-tests-starter/tstData/QDocTestRepository/src/resources/5.png new file mode 100644 index 00000000000..22dceff05be Binary files /dev/null and b/ui-tests-starter/tstData/QDocTestRepository/src/resources/5.png differ diff --git a/ui-tests-starter/tstData/QDocTestRepository/src/resources/6.png b/ui-tests-starter/tstData/QDocTestRepository/src/resources/6.png new file mode 100644 index 00000000000..8099115617b Binary files /dev/null and b/ui-tests-starter/tstData/QDocTestRepository/src/resources/6.png differ diff --git a/ui-tests-starter/tstData/QDocTestRepository/src/resources/7.png b/ui-tests-starter/tstData/QDocTestRepository/src/resources/7.png new file mode 100644 index 00000000000..2d6b3899f53 Binary files /dev/null and b/ui-tests-starter/tstData/QDocTestRepository/src/resources/7.png differ diff --git a/ui-tests-starter/tstData/QDocTestRepository/src/resources/8.png b/ui-tests-starter/tstData/QDocTestRepository/src/resources/8.png new file mode 100644 index 00000000000..af954f06a56 Binary files /dev/null and b/ui-tests-starter/tstData/QDocTestRepository/src/resources/8.png differ diff --git a/ui-tests-starter/tstData/QDocTestRepository/src/resources/9.png b/ui-tests-starter/tstData/QDocTestRepository/src/resources/9.png new file mode 100644 index 00000000000..9a1afc15e56 Binary files /dev/null and b/ui-tests-starter/tstData/QDocTestRepository/src/resources/9.png differ