Skip to content

Commit

Permalink
Add automated end-to-end testing
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexander01998 committed Nov 4, 2024
1 parent 5cafd52 commit 4e17d02
Show file tree
Hide file tree
Showing 7 changed files with 702 additions and 1 deletion.
50 changes: 50 additions & 0 deletions .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,53 @@ jobs:
echo "- [$filename]($url)" >> $GITHUB_STEP_SUMMARY
done
echo "</details>" >> $GITHUB_STEP_SUMMARY
- name: Run the mod and take screenshots
uses: modmuss50/xvfb-action@c56c7da0c8fc9a7cb5df2e50dd2a43a80b64c5cb
with:
run: ./gradlew runEndToEndTest --stacktrace --warning-mode=fail

# Needed because the screenshot gallery won't be created on pull requests.
# Also useful if Imgur uploads fail.
- name: Upload Test Screenshots.zip artifact
uses: actions/upload-artifact@v4
if: always()
with:
name: Test Screenshots
path: run/screenshots

- name: Create test screenshot gallery
if: always() && ${{ env.IMGUR_CLIENT_ID }}
shell: bash
run: |
echo "<details open>" >> $GITHUB_STEP_SUMMARY
echo "<summary>📸 Test Screenshots</summary>" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
for img in run/screenshots/*.png; do
if [ -f "$img" ]; then
filename=$(basename "$img")
# Remove timestamp and extension
simplified_name=$(echo "${filename%.*}" | cut -c 21-)
# Upload to Imgur
response=$(curl -s -X POST \
-H "Authorization: Client-ID $IMGUR_CLIENT_ID" \
-F "image=@$img" \
https://api.imgur.com/3/image)
# Extract the URL from the response
url=$(echo $response | grep -o '"link":"[^"]*"' | cut -d'"' -f4)
if [ ! -z "$url" ]; then
# Convert underscores to spaces and capitalize first letter of each word
title=$(echo "$simplified_name" | tr '_' ' ' | awk '{for(i=1;i<=NF;i++)sub(/./,toupper(substr($i,1,1)),$i)}1')
echo "### $title" >> $GITHUB_STEP_SUMMARY
echo "![${simplified_name}]($url)" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
else
echo "Failed to upload $filename" >> $GITHUB_STEP_SUMMARY
fi
fi
done
echo "</details>" >> $GITHUB_STEP_SUMMARY
49 changes: 49 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,55 @@ dependencies {
}
}

loom {
accessWidenerPath = file("src/main/resources/chestesp.accesswidener")
}

configurations {
productionRuntime {
extendsFrom configurations.minecraftLibraries
extendsFrom configurations.loaderLibraries
extendsFrom configurations.minecraftRuntimeLibraries
}
}

dependencies {
productionRuntime "net.fabricmc:fabric-loader:${project.loader_version}"
productionRuntime "net.fabricmc:intermediary:${project.minecraft_version}"
}

import net.fabricmc.loom.util.Platform
tasks.register('runEndToEndTest', JavaExec) {
dependsOn remapJar, downloadAssets
classpath.from configurations.productionRuntime
mainClass = "net.fabricmc.loader.impl.launch.knot.KnotClient"
workingDir = file("run")

doFirst {
classpath.from loom.minecraftProvider.minecraftClientJar
workingDir.mkdirs()

args(
"--assetIndex", loom.minecraftProvider.versionInfo.assetIndex().fabricId(loom.minecraftProvider.minecraftVersion()),
"--assetsDir", new File(loom.files.userCache, "assets").absolutePath,
"--gameDir", workingDir.absolutePath
)

if (Platform.CURRENT.operatingSystem.isMacOS()) {
jvmArgs("-XstartOnFirstThread")
}

jvmArgs(
"-Dfabric.addMods=${configurations.modImplementation.find { it.name.contains('fabric-api') }.absolutePath}${File.pathSeparator}${remapJar.archiveFile.get().asFile.absolutePath}",
"-Dchestesp.e2eTest",
"-Dfabric-tag-conventions-v2.missingTagTranslationWarning=fail",
"-Dfabric-tag-conventions-v1.legacyTagWarning=fail",
"-Dmixin.debug.verify=true",
"-Dmixin.debug.countInjections=true"
)
}
}

processResources {
inputs.property "version", project.version

Expand Down
118 changes: 118 additions & 0 deletions src/main/java/net/wimods/chestesp/test/ChestESPClientTestHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright (c) 2023-2024 Wurst-Imperium and contributors.
*
* This source code is subject to the terms of the GNU General Public
* License, version 3. If a copy of the GPL was not distributed with this
* file, You can obtain one at: https://www.gnu.org/licenses/gpl-3.0.txt
*/
package net.wimods.chestesp.test;

import static net.wimods.chestesp.test.fabric.FabricClientTestHelper.*;

import java.util.function.Consumer;

import net.minecraft.client.gui.Drawable;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ClickableWidget;
import net.minecraft.client.gui.widget.PressableWidget;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.client.gui.widget.Widget;
import net.wimods.chestesp.ChestEspConfig;
import net.wimods.chestesp.ChestEspMod;

public enum ChestESPClientTestHelper
{
;

public static void clickScreenButton(int x, int y)
{
waitFor("Click button at " + x + ", " + y, mc -> {
Screen screen = mc.currentScreen;
if(screen == null)
return false;

for(Drawable drawable : screen.drawables)
{
if(!(drawable instanceof Widget widget))
continue;

System.out.println(
"Found widget at " + widget.getX() + ", " + widget.getY()
+ " of type " + widget.getClass().getName());

if(widget instanceof PressableWidget pressable
&& pressMatchingButton(pressable, x, y))
return true;

widget.forEachChild(clickableWidget -> pressMatchingButton(
clickableWidget, x, y));
}

return false;
});
}

private static boolean pressMatchingButton(ClickableWidget widget, int x,
int y)
{
if(widget instanceof PressableWidget button && button.getX() == x
&& button.getY() == y)
{
button.onPress();
return true;
}

return false;
}

public static void setTextfieldText(int index, String text)
{
waitFor("Set textfield " + index + " to " + text, mc -> {
Screen screen = mc.currentScreen;
if(screen == null)
return false;

int currentIndex = 0;
for(Drawable drawable : screen.drawables)
{
if(!(drawable instanceof TextFieldWidget textField))
continue;

if(currentIndex == index)
{
textField.setText(text);
return true;
}

currentIndex++;
}

return false;
});
}

public static void runChatCommand(String command)
{
submitAndWait(mc -> {
mc.getNetworkHandler().sendChatCommand(command);
return null;
});
}

public static void clearChat()
{
submitAndWait(mc -> {
mc.inGameHud.getChatHud().clear(true);
return null;
});
}

public static void updateConfig(Consumer<ChestEspConfig> configUpdater)
{
submitAndWait(mc -> {
configUpdater.accept(
ChestEspMod.getInstance().getConfigHolder().getConfig());
return null;
});
}
}
Loading

0 comments on commit 4e17d02

Please sign in to comment.