feat: add in mocking to ui tests

This commit is contained in:
Ryan Dowling 2021-01-13 23:43:23 +11:00
parent 51e0a1372a
commit 26547f8671
No known key found for this signature in database
GPG key ID: 5539FCDB88950EFD
32 changed files with 5301 additions and 46 deletions

View file

@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-20.04
strategy:
matrix:
java-version: ["1.8", "1.9", "1.10", "1.11", "1.12", "1.13", "1.14"]
java-version: ["1.8", "1.9", "1.10", "1.11", "1.12", "1.13", "1.14", "1.15"]
steps:
- uses: actions/checkout@v2
@ -29,9 +29,6 @@ jobs:
- name: Test
uses: GabrielBB/xvfb-action@v1
env:
MOJANG_ACCOUNT_USERNAME: ${{ secrets.MOJANG_ACCOUNT_USERNAME }}
MOJANG_ACCOUNT_PASSWORD: ${{ secrets.MOJANG_ACCOUNT_PASSWORD }}
with:
run: ./gradlew test
build:

1
.gitignore vendored
View file

@ -23,3 +23,4 @@ cache/
# Other
java_pid*.hprof
*.log
mockserver_keystore_*

View file

@ -11,6 +11,7 @@
"naco-siren.gradle-language",
"rebornix.project-snippets",
"mrorz.language-gettext",
"aaron-bond.better-comments"
"aaron-bond.better-comments",
"mathiasfrohlich.kotlin"
]
}

2
.vscode/launch.json vendored
View file

@ -19,7 +19,7 @@
"projectName": "ATLauncher",
"cwd": "${workspaceFolder}/testLauncher/dev",
"preLaunchTask": "makeTestLauncherDevDirectory",
"args": "--debug --debug-level 5 --disable-error-reporting --no-launcher-update --base-launcher-domain=atlauncher.test --base-cdn-domain=files.atlauncher.test --base-cdn-path=/ --allow-all-ssl-certs"
"args": "--debug --debug-level 5 --disable-error-reporting --no-launcher-update --base-launcher-domain=https://atlauncher.test --base-cdn-domain=files.atlauncher.test --base-cdn-path=/ --allow-all-ssl-certs"
}
]
}

View file

@ -67,6 +67,7 @@ dependencies {
testImplementation 'junit:junit:4.13.1'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
testImplementation 'org.assertj:assertj-swing-junit:3.17.1'
testImplementation 'org.mock-server:mockserver-netty:5.11.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
testRuntimeOnly 'org.junit.vintage:junit-vintage-engine:5.7.0'
}

View file

@ -34,9 +34,15 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.SocketAddress;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Properties;
@ -137,6 +143,15 @@ public class App {
*/
public static boolean skipTrayIntegration = false;
/**
* This allows skipping the in built analytics collection. This is mainly useful
* for development when you don't want to report analytics. For end users, this
* can be turned off in the launcher setup or through the settings.
* <p/>
* --disable-analytics
*/
public static boolean disableAnalytics = false;
/**
* This allows skipping the in built error reporting. This is mainly useful for
* development when you don't want to report errors to an external third party.
@ -841,6 +856,7 @@ public class App {
parser.accepts("updated").withOptionalArg().ofType(Boolean.class);
parser.accepts("skip-setup-dialog").withOptionalArg().ofType(Boolean.class);
parser.accepts("skip-tray-integration").withOptionalArg().ofType(Boolean.class);
parser.accepts("disable-analytics").withOptionalArg().ofType(Boolean.class);
parser.accepts("disable-error-reporting").withOptionalArg().ofType(Boolean.class);
parser.accepts("skip-integration").withOptionalArg().ofType(Boolean.class);
parser.accepts("skip-hash-checking").withOptionalArg().ofType(Boolean.class);
@ -856,6 +872,9 @@ public class App {
parser.accepts("debug").withOptionalArg().ofType(Boolean.class);
parser.accepts("debug-level").withRequiredArg().ofType(Integer.class);
parser.accepts("launch").withRequiredArg().ofType(String.class);
parser.accepts("proxy-type").withRequiredArg().ofType(String.class);
parser.accepts("proxy-host").withRequiredArg().ofType(String.class);
parser.accepts("proxy-port").withRequiredArg().ofType(Integer.class);
OptionSet options = parser.parse(args);
autoLaunch = options.has("launch") ? (String) options.valueOf("launch") : null;
@ -885,6 +904,11 @@ public class App {
LogManager.debug("Skipping tray integration!");
}
disableAnalytics = options.has("disable-analytics");
if (disableAnalytics) {
LogManager.debug("Disabling analytics!");
}
disableErrorReporting = options.has("disable-error-reporting");
if (disableErrorReporting) {
LogManager.debug("Disabling error reporting!");
@ -950,5 +974,28 @@ public class App {
if (skipHashChecking) {
LogManager.debug("Skipping hash checking! Don't ask for support with this enabled!");
}
if (options.has("proxy-type") && options.has("proxy-host") && options.has("proxy-port")) {
String proxyType = String.valueOf(options.valueOf("proxy-type"));
String proxyHost = String.valueOf(options.valueOf("proxy-host"));
Integer proxyPort = (Integer) options.valueOf("proxy-port");
Proxy proxy = new java.net.Proxy(java.net.Proxy.Type.valueOf(proxyType),
new InetSocketAddress(proxyHost, proxyPort));
LogManager.warn("Proxy set to " + proxy);
ProxySelector.setDefault(new ProxySelector() {
@Override
public List<java.net.Proxy> select(URI uri) {
return Arrays.asList(proxy);
}
@Override
public void connectFailed(URI uri, SocketAddress sa, IOException e) {
LogManager.logStackTrace("Connection could not be established to proxy at socket [" + sa + "]", e);
}
});
}
}
}

View file

@ -53,18 +53,20 @@ public class Constants {
public static final String SENTRY_DSN = "https://499c3bbc55cb434dad42a3ac670e2c91@sentry.io/1498519";
// Launcher domains, endpoints, etc
public static String BASE_LAUNCHER_PROTOCOL = "https://";
public static String BASE_LAUNCHER_DOMAIN = "atlauncher.com";
public static String API_BASE_URL = "https://api." + BASE_LAUNCHER_DOMAIN + "/v1/launcher/";
public static String API_BASE_URL = BASE_LAUNCHER_PROTOCOL + "api." + BASE_LAUNCHER_DOMAIN + "/v1/launcher/";
public static String API_HOST = "api." + BASE_LAUNCHER_DOMAIN;
public static String PASTE_CHECK_URL = "https://paste." + BASE_LAUNCHER_DOMAIN;
public static String PASTE_CHECK_URL = BASE_LAUNCHER_PROTOCOL + "paste." + BASE_LAUNCHER_DOMAIN;
public static String PASTE_HOST = "paste." + BASE_LAUNCHER_DOMAIN;
public static String SERVERS_LIST_PACK = "https://" + BASE_LAUNCHER_DOMAIN + "/servers/list/pack";
public static String PASTE_API_URL = "https://paste." + BASE_LAUNCHER_DOMAIN + "/api/create";
public static String SERVERS_LIST_PACK = BASE_LAUNCHER_PROTOCOL + BASE_LAUNCHER_DOMAIN + "/servers/list/pack";
public static String PASTE_API_URL = BASE_LAUNCHER_PROTOCOL + "paste." + BASE_LAUNCHER_DOMAIN + "/api/create";
// CDN domains, endpoints, etc
public static String BASE_CDN_PROTOCOL = "https://";
public static String BASE_CDN_DOMAIN = "download.nodecdn.net";
public static String BASE_CDN_PATH = "/containers/atl";
public static String DOWNLOAD_SERVER = "https://" + BASE_CDN_DOMAIN + BASE_CDN_PATH;
public static String DOWNLOAD_SERVER = BASE_CDN_PROTOCOL + BASE_CDN_DOMAIN + BASE_CDN_PATH;
public static String DOWNLOAD_HOST = BASE_CDN_DOMAIN;
// CurseForge domains, endpoints, config, etc
@ -126,23 +128,29 @@ public class Constants {
public static final String MICROSOFT_MINECRAFT_PROFILE_URL = "https://api.minecraftservices.com/minecraft/profile";
public static void setBaseLauncherDomain(String baseLauncherDomain) {
BASE_LAUNCHER_DOMAIN = baseLauncherDomain;
API_BASE_URL = "https://api." + baseLauncherDomain + "/v1/launcher/";
API_HOST = "api." + baseLauncherDomain;
PASTE_CHECK_URL = "https://paste." + baseLauncherDomain;
PASTE_HOST = "paste." + baseLauncherDomain;
SERVERS_LIST_PACK = "https://" + baseLauncherDomain + "/servers/list/pack";
PASTE_API_URL = "https://paste." + baseLauncherDomain + "/api/create";
String host = baseLauncherDomain.replace("https://", "").replace("http://", "");
BASE_LAUNCHER_PROTOCOL = baseLauncherDomain.startsWith("https://") ? "https://" : "http://";
BASE_LAUNCHER_DOMAIN = host;
API_BASE_URL = BASE_LAUNCHER_PROTOCOL + "api." + host + "/v1/launcher/";
API_HOST = "api." + host;
PASTE_CHECK_URL = BASE_LAUNCHER_PROTOCOL + "paste." + host;
PASTE_HOST = "paste." + host;
SERVERS_LIST_PACK = BASE_LAUNCHER_PROTOCOL + host + "/servers/list/pack";
PASTE_API_URL = BASE_LAUNCHER_PROTOCOL + "paste." + host + "/api/create";
}
public static void setBaseCdnDomain(String baseCdnDomain) {
BASE_CDN_DOMAIN = baseCdnDomain;
DOWNLOAD_SERVER = "https://" + baseCdnDomain + BASE_CDN_PATH;
DOWNLOAD_HOST = baseCdnDomain;
String host = baseCdnDomain.replace("https://", "").replace("http://", "");
BASE_CDN_PROTOCOL = baseCdnDomain.startsWith("https://") ? "https://" : "http://";
BASE_CDN_DOMAIN = host;
DOWNLOAD_SERVER = baseCdnDomain + BASE_CDN_PATH;
DOWNLOAD_HOST = host;
}
public static void setBaseCdnPath(String baseCdnPath) {
BASE_CDN_PATH = baseCdnPath;
DOWNLOAD_SERVER = "https://" + BASE_CDN_DOMAIN + baseCdnPath;
DOWNLOAD_SERVER = BASE_CDN_PROTOCOL + BASE_CDN_DOMAIN + baseCdnPath;
}
}

View file

@ -111,7 +111,7 @@ public class SetupDialog extends JDialog implements RelocalizationListener {
gbc.gridx++;
gbc.anchor = GridBagConstraints.BASELINE_LEADING;
enableAnalytics = new JCheckBox();
enableAnalytics.setSelected(true);
enableAnalytics.setSelected(!App.disableAnalytics);
middle.add(enableAnalytics, gbc);
// Bottom Panel Stuff

View file

@ -50,6 +50,7 @@ public class PackManager {
public static void loadPacks() {
PerformanceManager.start();
LogManager.debug("Loading packs");
Data.PACKS.clear();
try {
java.lang.reflect.Type type = new TypeToken<List<Pack>>() {
}.getType();

View file

@ -34,7 +34,7 @@ public final class Analytics implements SettingsListener {
ga = GoogleAnalytics.builder()
.withConfig(new GoogleAnalyticsConfig().setDiscoverRequestParameters(true)
.setProxyHost(App.settings.proxyHost).setProxyPort(App.settings.proxyPort)
.setEnabled(App.settings.enableAnalytics))
.setEnabled(!App.disableAnalytics && App.settings.enableAnalytics))
.withDefaultRequest(new DefaultRequest().userAgent(Network.USER_AGENT)
.clientId(App.settings.analyticsClientId).customDimension(1, Java.getLauncherJavaVersion()))
.withTrackingId(Constants.GA_TRACKING_ID).withAppName(Constants.LAUNCHER_NAME)
@ -101,6 +101,6 @@ public final class Analytics implements SettingsListener {
@Override
public void onSettingsSaved() {
ga.getConfig().setProxyHost(App.settings.proxyHost).setProxyPort(App.settings.proxyPort)
.setEnabled(App.settings.enableAnalytics);
.setEnabled(!App.disableAnalytics && App.settings.enableAnalytics);
}
}

View file

@ -22,8 +22,11 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
import javax.net.ssl.HttpsURLConnection;
import com.atlauncher.App;
import com.atlauncher.constants.Constants;
import com.atlauncher.ui.mocks.MockHelper;
import com.atlauncher.utils.FileUtils;
import org.assertj.swing.core.GenericTypeMatcher;
@ -35,10 +38,15 @@ import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.mockserver.integration.ClientAndServer;
import org.mockserver.logging.MockServerLogger;
import org.mockserver.socket.PortFactory;
import org.mockserver.socket.tls.KeyStoreFactory;
public class AbstractUiTest extends AssertJSwingTestCaseTemplate {
// get a working directory specifically for this run
protected static final Path workingDir = Paths.get("testLauncher/ui-tests/" + UUID.randomUUID()).toAbsolutePath();
protected ClientAndServer mockServer;
protected FrameFixture frame;
@ -56,11 +64,26 @@ public class AbstractUiTest extends AssertJSwingTestCaseTemplate {
public final void setUp() {
preSetUp();
HttpsURLConnection.setDefaultSSLSocketFactory(
new KeyStoreFactory(new MockServerLogger()).sslContext().getSocketFactory());
mockServer = ClientAndServer.startClientAndServer(PortFactory.findFreePort());
if (System.getenv("CI") == null || !System.getenv("CI").equalsIgnoreCase("true")) {
mockServer.openUI();
}
this.setUpRobot();
this.setupHttpMocks();
// start the application
ApplicationLauncher.application(App.class).withArgs("--skip-setup-dialog", "--skip-integration",
"--skip-tray-integration", "--no-launcher-update", "--working-dir=" + workingDir.toString()).start();
ApplicationLauncher.application(App.class)
.withArgs("--skip-setup-dialog", "--skip-integration", "--disable-analytics",
"--disable-error-reporting", "--skip-tray-integration", "--no-launcher-update",
"--proxy-type=SOCKS", "--proxy-host=127.0.0.1", "--proxy-port=" + mockServer.getPort(),
"--working-dir=" + workingDir.toString())
.start();
// get a reference to the main launcher frame
frame = WindowFinder.findFrame(new GenericTypeMatcher<Frame>(Frame.class) {
@ -79,6 +102,19 @@ public class AbstractUiTest extends AssertJSwingTestCaseTemplate {
onSetUp();
}
private void setupHttpMocks() {
// files.json
MockHelper.mockFilesJson(mockServer);
// files to download
MockHelper.mockFileResponse(mockServer, "newnews.json");
MockHelper.mockFileResponse(mockServer, "runtimes.json");
MockHelper.mockFileResponse(mockServer, "users.json");
MockHelper.mockFileResponse(mockServer, "minecraft.json");
MockHelper.mockFileResponse(mockServer, "packsnew.json");
MockHelper.mockFileResponse(mockServer, "version.json");
}
protected void onSetUp() {
}
@ -86,6 +122,7 @@ public class AbstractUiTest extends AssertJSwingTestCaseTemplate {
public final void tearDown() {
try {
onTearDown();
mockServer.stop(true);
} finally {
cleanUp();
}

View file

@ -21,8 +21,10 @@ import java.awt.Dialog;
import java.nio.file.Files;
import java.util.concurrent.TimeUnit;
import com.atlauncher.constants.Constants;
import com.atlauncher.gui.card.InstanceCard;
import com.atlauncher.gui.card.PackCard;
import com.atlauncher.ui.mocks.MockHelper;
import org.assertj.swing.core.GenericTypeMatcher;
import org.assertj.swing.core.matcher.JButtonMatcher;
@ -48,11 +50,7 @@ public class BasicLauncherUiTest extends AbstractUiTest {
mainTabsFixture.requireVisible();
mainTabsFixture.requireTitle("News", Index.atIndex(0));
mainTabsFixture.requireSelectedTab(Index.atIndex(0));
}
@Test
public void testThatUsersCanLogin() {
JTabbedPaneFixture mainTabsFixture = this.frame.tabbedPane("mainTabs");
mainTabsFixture.selectTab("Accounts");
JComboBoxFixture accountsTabAccountsComboBox = this.frame.comboBox("accountsTabAccountsComboBox");
@ -69,8 +67,15 @@ public class BasicLauncherUiTest extends AbstractUiTest {
JTextComponentFixture passwordField = this.frame.textBox("passwordField");
passwordField.requireVisible().requireEditable();
usernameField.setText(System.getenv("MOJANG_ACCOUNT_USERNAME"));
passwordField.setText(System.getenv("MOJANG_ACCOUNT_PASSWORD"));
MockHelper.mockJson(mockServer, "POST", "authserver.mojang.com", "/authenticate", "login-success.js");
MockHelper.mockJson(mockServer, "GET", "sessionserver.mojang.com",
"/session/minecraft/profile/e50e5b562ca3c41f35631867a7cb14c5", "profile.json");
MockHelper.mockPng(mockServer, "GET", "textures.minecraft.net",
"/texture/3b60a1f6d562f52aaebbf1434f1de147933a3affe0e764fa49ea057536623cd3",
"3b60a1f6d562f52aaebbf1434f1de147933a3affe0e764fa49ea057536623cd3.png");
usernameField.setText("test@example.com");
passwordField.setText("password");
// login
loginButton.click();
@ -78,7 +83,7 @@ public class BasicLauncherUiTest extends AbstractUiTest {
DialogFixture loginDialog = WindowFinder.findDialog("loginDialog").using(robot());
loginDialog.requireVisible();
// give it time
// give it 5 seconds
Pause.pause(5, TimeUnit.SECONDS);
// account was added, fields cleared
@ -89,22 +94,36 @@ public class BasicLauncherUiTest extends AbstractUiTest {
// account selector now showing
JComboBoxFixture accountSelector = this.frame.comboBox("accountSelector");
accountSelector.requireVisible();
}
@Test
public void testThatInstancesCanInstall() {
JTabbedPaneFixture mainTabsFixture = this.frame.tabbedPane("mainTabs");
mainTabsFixture.selectTab("Vanilla Packs");
MockHelper.mockCdnJson(mockServer, "GET", "/containers/atl/packs/VanillaMinecraft/versions/1.16.4/Configs.json",
"vanilla-1-16-4-configs.json");
MockHelper.mockJson(mockServer, "GET", "launchermeta.mojang.com", "/mc/game/version_manifest.json",
"version_manifest.json");
MockHelper.mockJson(mockServer, "GET", "launchermeta.mojang.com",
"/v1/packages/3c33166875193f50f446a0730960208fcbf9f96c/1.16.4.json", "1.16.4.json");
MockHelper.mockJson(mockServer, "GET", "launchermeta.mojang.com",
"/v1/packages/f8e11ca03b475dd655755b945334c7a0ac2c3b43/1.16.json", "1.16.json");
MockHelper.mockPng(mockServer, "GET", "resources.download.minecraft.net",
"/1b/1b5fa6ad7c204f60654d43fa25560ff36a6420dc", "1b5fa6ad7c204f60654d43fa25560ff36a6420dc");
MockHelper.mockPng(mockServer, "GET", "resources.download.minecraft.net",
"/a8/a81ca0f94145275865aca5b27df09b17836bc102", "a81ca0f94145275865aca5b27df09b17836bc102");
MockHelper.mockJar(mockServer, "GET", "libraries.minecraft.net", "/com/atlauncher/test/1.0/test-1.0.jar",
"test-1.0.jar");
MockHelper.mockXml(mockServer, "GET", "launcher.mojang.com",
"/v1/objects/9150e6e5de6d49a83113ed3be5719aed2a387523/client-1.12.xml", "client-1.12.xml");
MockHelper.mockJar(mockServer, "GET", "launcher.mojang.com",
"/v1/objects/4addb91039ae452c5612f288bfe6ce925dac92c5/client.jar", "client-1-16-4.jar");
MockHelper.mockNoResponseSuccess(mockServer, "POST", Constants.API_HOST,
"/v1/launcher/pack/VanillaMinecraft/installed/");
JPanelFixture vanillaPacksPanel = this.frame.panel("vanillaPacksPanel");
vanillaPacksPanel.requireVisible();
// give it time
Pause.pause(5, TimeUnit.SECONDS);
JPanelFixture vanillaPackCard = vanillaPacksPanel.panel(new GenericTypeMatcher<PackCard>(PackCard.class) {
JPanelFixture vanillaPackCard = vanillaPacksPanel.panel(new GenericTypeMatcher<PackCard>(PackCard.class, true) {
@Override
protected boolean isMatching(PackCard packCard) {
System.out.println(packCard);
return packCard.getPack().name.equalsIgnoreCase("Vanilla Minecraft") && packCard.isVisible();
}
});
@ -114,10 +133,10 @@ public class BasicLauncherUiTest extends AbstractUiTest {
newInstanceButton.requireVisible();
newInstanceButton.click();
DialogFixture loginDialog = WindowFinder.findDialog("instanceInstallerDialog").using(robot());
loginDialog.requireVisible();
DialogFixture instanceInstallerDialog = WindowFinder.findDialog("instanceInstallerDialog").using(robot());
instanceInstallerDialog.requireVisible();
JButtonFixture installButton = loginDialog.button(JButtonMatcher.withText("Install"));
JButtonFixture installButton = instanceInstallerDialog.button(JButtonMatcher.withText("Install"));
installButton.requireVisible();
installButton.click();

View file

@ -0,0 +1,206 @@
/*
* ATLauncher - https://github.com/ATLauncher/ATLauncher
* Copyright (C) 2013-2021 ATLauncher
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.atlauncher.ui.mocks;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.atlauncher.Gsons;
import com.atlauncher.constants.Constants;
import com.atlauncher.data.DownloadableFile;
import com.atlauncher.utils.Hashing;
import org.mockserver.client.ForwardChainExpectation;
import org.mockserver.integration.ClientAndServer;
import org.mockserver.model.BinaryBody;
import org.mockserver.model.HttpRequest;
import org.mockserver.model.HttpResponse;
import org.mockserver.model.HttpTemplate;
public class MockHelper {
public static Map<String, Path> mockedFilePaths = new HashMap<>();
static {
mockedFilePaths.put("newnews.json", Paths.get("src/test/resources/mocks/download-nodecdn-net/newnews.json"));
mockedFilePaths.put("runtimes.json", Paths.get("src/test/resources/mocks/download-nodecdn-net/runtimes.json"));
mockedFilePaths.put("users.json", Paths.get("src/test/resources/mocks/download-nodecdn-net/users.json"));
mockedFilePaths.put("minecraft.json",
Paths.get("src/test/resources/mocks/download-nodecdn-net/minecraft.json"));
mockedFilePaths.put("packsnew.json", Paths.get("src/test/resources/mocks/download-nodecdn-net/packsnew.json"));
mockedFilePaths.put("version.json", Paths.get("src/test/resources/mocks/download-nodecdn-net/version.json"));
}
public static void mockFilesJson(ClientAndServer mockServer) {
List<DownloadableFile> downloadableFiles = new ArrayList<>();
try {
DownloadableFile launcher = new DownloadableFile();
launcher.name = "launcher";
launcher.folder = "launcher";
launcher.size = 0;
launcher.sha1 = Constants.VERSION.toStringForLogging();
downloadableFiles.add(launcher);
DownloadableFile newNews = new DownloadableFile();
newNews.name = "newnews.json";
newNews.folder = "json";
newNews.size = (int) Files.size(mockedFilePaths.get("newnews.json"));
newNews.sha1 = Hashing.sha1(mockedFilePaths.get("newnews.json")).toString();
downloadableFiles.add(newNews);
DownloadableFile runtimes = new DownloadableFile();
runtimes.name = "runtimes.json";
runtimes.folder = "json";
runtimes.size = (int) Files.size(mockedFilePaths.get("runtimes.json"));
runtimes.sha1 = Hashing.sha1(mockedFilePaths.get("runtimes.json")).toString();
downloadableFiles.add(runtimes);
DownloadableFile users = new DownloadableFile();
users.name = "users.json";
users.folder = "json";
users.size = (int) Files.size(mockedFilePaths.get("users.json"));
users.sha1 = Hashing.sha1(mockedFilePaths.get("users.json")).toString();
downloadableFiles.add(users);
DownloadableFile minecraft = new DownloadableFile();
minecraft.name = "minecraft.json";
minecraft.folder = "json";
minecraft.size = (int) Files.size(mockedFilePaths.get("minecraft.json"));
minecraft.sha1 = Hashing.sha1(mockedFilePaths.get("minecraft.json")).toString();
downloadableFiles.add(minecraft);
DownloadableFile packsNew = new DownloadableFile();
packsNew.name = "packsnew.json";
packsNew.folder = "json";
packsNew.size = (int) Files.size(mockedFilePaths.get("packsnew.json"));
packsNew.sha1 = Hashing.sha1(mockedFilePaths.get("packsnew.json")).toString();
downloadableFiles.add(packsNew);
DownloadableFile version = new DownloadableFile();
version.name = "version.json";
version.folder = "json";
version.size = (int) Files.size(mockedFilePaths.get("version.json"));
version.sha1 = Hashing.sha1(mockedFilePaths.get("version.json")).toString();
downloadableFiles.add(version);
} catch (IOException e) {
e.printStackTrace();
}
mockServer.when(HttpRequest.request().withMethod("GET").withPath("/containers/atl/launcher/json/files.json"))
.respond(HttpResponse.response().withStatusCode(200).withHeader("Content-Type", "application/json")
.withBody(Gsons.DEFAULT.toJson(downloadableFiles)));
}
public static void mockFileResponse(ClientAndServer mockServer, String string) {
mockCdnJson(mockServer, "GET", "/containers/atl/launcher/json/" + string, string);
}
public static void mockNoResponseSuccess(ClientAndServer mockServer, String method, String host, String path) {
mockServer.when(HttpRequest.request().withMethod(method).withHeader("Host", host).withPath(path))
.respond(HttpResponse.response().withStatusCode(200).withHeader("Content-Type", "application/json"));
}
public static void mockCdnJson(ClientAndServer mockServer, String method, String path, String responseFile) {
mock(mockServer, method, Constants.BASE_CDN_DOMAIN, path, responseFile, ResponseType.JSON);
}
public static void mockJson(ClientAndServer mockServer, String method, String host, String path,
String responseFile) {
mock(mockServer, method, host, path, responseFile,
responseFile.endsWith(".js") ? ResponseType.JAVASCRIPT : ResponseType.JSON);
}
public static void mockPng(ClientAndServer mockServer, String method, String host, String path,
String responseFile) {
mock(mockServer, method, host, path, responseFile, ResponseType.PNG);
}
public static void mockJar(ClientAndServer mockServer, String method, String host, String path,
String responseFile) {
mock(mockServer, method, host, path, responseFile, ResponseType.JAR);
}
public static void mockXml(ClientAndServer mockServer, String method, String host, String path,
String responseFile) {
mock(mockServer, method, host, path, responseFile, ResponseType.XML);
}
public static void mockTxt(ClientAndServer mockServer, String method, String host, String path,
String responseFile) {
mock(mockServer, method, host, path, responseFile, ResponseType.TXT);
}
public static void mock(ClientAndServer mockServer, String method, String host, String path, String responseFile,
ResponseType responseType) {
try {
Path filePath = Paths
.get(String.format("src/test/resources/mocks/%s/%s", host.replace(".", "-"), responseFile));
ForwardChainExpectation expectation = mockServer
.when(HttpRequest.request().withMethod(method).withHeader("Host", host).withPath(path));
switch (responseType) {
case JAVASCRIPT:
expectation.respond(HttpTemplate.template(HttpTemplate.TemplateType.JAVASCRIPT,
new String(Files.readAllBytes(filePath), StandardCharsets.UTF_8)));
break;
case PNG:
case XML:
case JAR:
case TXT:
expectation.respond(HttpResponse.response().withStatusCode(200)
.withHeader("Content-Type", getContentType(responseType))
.withHeader("Content-Disposition",
"form-data; name=\"" + responseFile + "\"; filename=\"" + responseFile + "\"")
.withBody(BinaryBody.binary(Files.readAllBytes(filePath))));
break;
case JSON:
default:
expectation.respond(
HttpResponse.response().withStatusCode(200).withHeader("Content-Type", "application/json")
.withBody(new String(Files.readAllBytes(filePath), StandardCharsets.UTF_8)));
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static String getContentType(ResponseType responseType) {
switch (responseType) {
case JAR:
return "application/java-archive";
case XML:
return "application/xml";
case TXT:
return "plain/text";
case PNG:
return "image/png";
case JSON:
default:
return "application/json";
}
}
}

View file

@ -0,0 +1,5 @@
package com.atlauncher.ui.mocks;
public enum ResponseType {
PNG, JAVASCRIPT, JSON, JAR, TXT, XML
}

View file

@ -0,0 +1,25 @@
return {
statusCode: 200,
headers: {
"Content-Type": ["application/json"],
},
body: JSON.stringify({
user: {
id: "e7d501015f383003a85959995366a06a",
username: "test@example.com",
},
accessToken:
"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJlN2Q1MDEwMTVmMzgzMDAzYTg1OTU5OTk1MzY2YTA2YSIsInlnZ3QiOiJjZDk5ZWZlNWM5YTE2NzRkYmNiOGE0NTFkN2ZiOWZiMiIsInNwciI6ImU1MGU1YjU2MmNhM2M0MWYzNTYzMTg2N2E3Y2IxNGM1IiwiaXNzIjoiWWdnZHJhc2lsLUF1dGgiLCJleHAiOjE2MTA3MDA5OTEsImlhdCI6MTYxMDUyODE5MX0.FeavRu8RQWFX_uQCmXlSEmF9J3pgZOB4X-_fRW7QwZk",
clientToken: request.body.clientToken,
selectedProfile: {
name: "Example",
id: "e50e5b562ca3c41f35631867a7cb14c5",
},
availableProfiles: [
{
name: "Example",
id: "e50e5b562ca3c41f35631867a7cb14c5",
},
],
}),
};

View file

@ -0,0 +1,776 @@
[
{
"version": "1.16.4",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.16.3",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.16.2",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.16.1",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.16",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.15.2",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.15.1",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.15",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.7.8",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.7.7",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.7.6",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.7.3",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "b1.3_01",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "rd-20090515",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.14.4",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.14.3",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.14.2",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.14.1",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.14",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.13.2",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.13.1",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.13",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.12.2",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.12.1",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.12",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.11.2",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.11.1",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.11",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.10.2",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.10.1",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.10",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.9.4",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.9.3",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.9.2",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.9.1",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.9",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.8.9",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.8.8",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.8.7",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.8.6",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.8.5",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.8.4",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.8.3",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.8.2",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.8.1",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.8",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.7.10",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.7.9",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.7.5",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.7.4",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.7.2",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.6.4",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.6.2",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.6.1",
"server": true,
"coremods": false,
"snapshot": false
},
{
"version": "1.5.2",
"server": true,
"coremods": true,
"snapshot": false
},
{
"version": "1.5.1",
"server": true,
"coremods": true,
"snapshot": false
},
{
"version": "1.4.7",
"server": true,
"coremods": true,
"snapshot": false
},
{
"version": "1.4.6",
"server": true,
"coremods": true,
"snapshot": false
},
{
"version": "1.4.5",
"server": true,
"coremods": true,
"snapshot": false
},
{
"version": "1.4.4",
"server": true,
"coremods": true,
"snapshot": false
},
{
"version": "1.4.2",
"server": true,
"coremods": true,
"snapshot": false
},
{
"version": "1.3.2",
"server": true,
"coremods": true,
"snapshot": false
},
{
"version": "1.3.1",
"server": true,
"coremods": true,
"snapshot": false
},
{
"version": "1.2.5",
"server": true,
"coremods": true,
"snapshot": false
},
{
"version": "1.2.4",
"server": false,
"coremods": true,
"snapshot": false
},
{
"version": "1.2.3",
"server": false,
"coremods": true,
"snapshot": false
},
{
"version": "1.2.2",
"server": false,
"coremods": true,
"snapshot": false
},
{
"version": "1.2.1",
"server": false,
"coremods": true,
"snapshot": false
},
{
"version": "1.1",
"server": false,
"coremods": true,
"snapshot": false
},
{
"version": "1.0",
"server": false,
"coremods": true,
"snapshot": false
},
{
"version": "b1.8.1",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "b1.8",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "b1.7.3",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "b1.7.2",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "b1.7",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "b1.6.6",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "b1.6.5",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "b1.6.4",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "b1.6.3",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "b1.6.2",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "b1.6.1",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "b1.6",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "b1.5_01",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "b1.5",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "b1.4_01",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "b1.4",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "b1.3b",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "b1.2_02",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "b1.2_01",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "b1.2",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "b1.1_02",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "b1.1_01",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "b1.0.2",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "b1.0_01",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "b1.0",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "a1.2.6",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "a1.2.5",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "a1.2.4_01",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "a1.2.3_04",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "a1.2.3_02",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "a1.2.3_01",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "a1.2.3",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "a1.2.2b",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "a1.2.2a",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "a1.2.1_01",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "a1.2.1",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "a1.2.0_02",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "a1.2.0_01",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "a1.2.0",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "a1.1.2_01",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "a1.1.2",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "a1.1.0",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "a1.0.17_04",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "a1.0.17_02",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "a1.0.16",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "a1.0.15",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "a1.0.14",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "a1.0.11",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "a1.0.5_01",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "a1.0.4",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "inf-20100618",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "c0.30_01c",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "c0.0.13a_03",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "c0.0.13a",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "c0.0.11a",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "rd-161348",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "rd-160052",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "rd-132328",
"server": false,
"coremods": false,
"snapshot": false
},
{
"version": "rd-132211",
"server": false,
"coremods": false,
"snapshot": false
}
]

View file

@ -0,0 +1,7 @@
[
{
"title": "I've been mocked :O",
"content": "I've been mocked, and if you're reading this, then good for you!!!!!",
"created_at": "2021-01-09T02:00:26.000000Z"
}
]

View file

@ -0,0 +1,25 @@
[
{
"id": 7,
"position": 8,
"name": "Vanilla Minecraft",
"type": "public",
"devVersions": [],
"versions": [
{
"version": "1.16.4",
"minecraft": "1.16.4",
"canUpdate": true,
"isRecommended": true
}
],
"createServer": true,
"logging": true,
"featured": false,
"system": true,
"hasDiscordImage": false,
"description": "Play Minecraft the way it was intended with Vanilla Minecraft. No mods, just plain simple Minecraft as Mojang intended you to play.",
"supportURL": "http://www.minecraft.net",
"websiteURL": "http://www.minecraft.net"
}
]

View file

@ -0,0 +1,24 @@
{
"windows": {
"x86": {
"version": "1.8.0_51",
"url": "runtimes/jre-win-32-1.8.0_51.xz",
"sha1": "e09ffca4f233398e186ee6ca514ea426a380a607",
"size": 38942464
},
"x64": {
"version": "1.8.0_51",
"url": "runtimes/jre-win-64-1.8.0_51.xz",
"sha1": "8a8bdecc9ea1bd361d0cb621c81850b67cb57692",
"size": 41251360
}
},
"osx": {
"x64": {
"version": "1.8.0_51",
"url": "runtimes/jre-osx-64-1.8.0_51.xz",
"sha1": "146bf64df14342388f4159cfaca1751202dd8837",
"size": 39026768
}
}
}

View file

@ -0,0 +1 @@
[]

View file

@ -0,0 +1,7 @@
{
"enableCurseIntegration": true,
"enableEditingMods": true,
"version": "1.16.4",
"minecraft": "1.16.4",
"noConfigs": true
}

View file

@ -0,0 +1,8 @@
{
"reserved": "3",
"major": "4",
"minor": "2",
"revision": "3",
"build": "0",
"stream": "release"
}

View file

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
</Configuration>

View file

@ -0,0 +1,155 @@
{
"arguments": {
"game": [
"--username",
"${auth_player_name}",
"--version",
"${version_name}",
"--gameDir",
"${game_directory}",
"--assetsDir",
"${assets_root}",
"--assetIndex",
"${assets_index_name}",
"--uuid",
"${auth_uuid}",
"--accessToken",
"${auth_access_token}",
"--userType",
"${user_type}",
"--versionType",
"${version_type}",
{
"rules": [
{
"action": "allow",
"features": {
"is_demo_user": true
}
}
],
"value": "--demo"
},
{
"rules": [
{
"action": "allow",
"features": {
"has_custom_resolution": true
}
}
],
"value": [
"--width",
"${resolution_width}",
"--height",
"${resolution_height}"
]
}
],
"jvm": [
{
"rules": [
{
"action": "allow",
"os": {
"name": "osx"
}
}
],
"value": [
"-XstartOnFirstThread"
]
},
{
"rules": [
{
"action": "allow",
"os": {
"name": "windows"
}
}
],
"value": "-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump"
},
{
"rules": [
{
"action": "allow",
"os": {
"name": "windows",
"version": "^10\\."
}
}
],
"value": [
"-Dos.name=Windows 10",
"-Dos.version=10.0"
]
},
{
"rules": [
{
"action": "allow",
"os": {
"arch": "x86"
}
}
],
"value": "-Xss1M"
},
"-Djava.library.path=${natives_directory}",
"-Dminecraft.launcher.brand=${launcher_name}",
"-Dminecraft.launcher.version=${launcher_version}",
"-cp",
"${classpath}"
]
},
"assetIndex": {
"id": "1.16",
"sha1": "3b67bf61fbd049c1fb5481e9a015626ae1a859eb",
"size": 249,
"totalSize": 2017,
"url": "https://launchermeta.mojang.com/v1/packages/f8e11ca03b475dd655755b945334c7a0ac2c3b43/1.16.json"
},
"assets": "1.16",
"complianceLevel": 1,
"downloads": {
"client": {
"sha1": "4addb91039ae452c5612f288bfe6ce925dac92c5",
"size": 318,
"url": "https://launcher.mojang.com/v1/objects/4addb91039ae452c5612f288bfe6ce925dac92c5/client.jar"
}
},
"id": "1.16.4",
"libraries": [
{
"downloads": {
"artifact": {
"path": "com/atlauncher/test/1.0/test-1.0.jar",
"sha1": "ef28203c29fe08638c2e685286427f00a4791962",
"size": 313,
"url": "https://libraries.minecraft.net/com/atlauncher/test/1.0/test-1.0.jar"
}
},
"name": "com.atlauncher:test:1.0"
}
],
"logging": {
"client": {
"argument": "-Dlog4j.configurationFile=${path}",
"file": {
"id": "client-1.12.xml",
"sha1": "9150e6e5de6d49a83113ed3be5719aed2a387523",
"size": 86,
"url": "https://launcher.mojang.com/v1/objects/9150e6e5de6d49a83113ed3be5719aed2a387523/client-1.12.xml"
},
"type": "log4j2-xml"
}
},
"mainClass": "net.minecraft.client.main.Main",
"minimumLauncherVersion": 21,
"releaseTime": "2020-10-29T15:49:37+00:00",
"time": "2020-10-29T15:49:37+00:00",
"type": "release"
}

View file

@ -0,0 +1,12 @@
{
"objects": {
"icons/icon_16x16.png": {
"hash": "1b5fa6ad7c204f60654d43fa25560ff36a6420dc",
"size": 694
},
"icons/icon_32x32.png": {
"hash": "a81ca0f94145275865aca5b27df09b17836bc102",
"size": 1323
}
}
}

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 694 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,10 @@
{
"id": "e50e5b562ca3c41f35631867a7cb14c5",
"name": "Example",
"properties": [
{
"name": "textures",
"value": "ewogICJ0aW1lc3RhbXAiIDogMTYxMDUyODE5Njk5NiwKICAicHJvZmlsZUlkIiA6ICJlNTBlNWI1NjJjYTNjNDFmMzU2MzE4NjdhN2NiMTRjNSIsCiAgInByb2ZpbGVOYW1lIiA6ICJFeGFtcGxlIiwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzNiNjBhMWY2ZDU2MmY1MmFhZWJiZjE0MzRmMWRlMTQ3OTMzYTNhZmZlMGU3NjRmYTQ5ZWEwNTc1MzY2MjNjZDMiLAogICAgICAibWV0YWRhdGEiIDogewogICAgICAgICJtb2RlbCIgOiAic2xpbSIKICAgICAgfQogICAgfQogIH0KfQ=="
}
]
}