mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-03-21 18:04:39 -05:00
Build Analyzer: Create new build prompt
This commit is contained in:
parent
cc7d0bfc7c
commit
27cb5be472
|
|
@ -104,7 +104,7 @@ export function AbilitiesSelector({
|
|||
})}
|
||||
type="button"
|
||||
onClick={() => onButtonClick(ability)}
|
||||
data-cy={`${ability.name}-ability-button`}
|
||||
data-testid={`${ability.name}-ability-button`}
|
||||
draggable="true"
|
||||
onDragStart={onDragStart(ability)}
|
||||
onDragEnd={onDragEnd}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ export function Ability({
|
|||
} as any
|
||||
}
|
||||
onClick={onClick}
|
||||
data-cy={`${ability}-ability`}
|
||||
data-testid={`${ability}-ability`}
|
||||
onDragOver={onDragOver}
|
||||
onDragLeave={onDragLeave}
|
||||
onDrop={(event) => {
|
||||
|
|
|
|||
|
|
@ -61,3 +61,5 @@ export const multiShot: Partial<Record<MainWeaponId, number>> = {
|
|||
};
|
||||
|
||||
export const RAINMAKER_SPEED_PENALTY_MODIFIER = 0.8;
|
||||
|
||||
export const UNKNOWN_SHORT = "U";
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
import { useSearchParams } from "@remix-run/react";
|
||||
import { EMPTY_BUILD } from "~/constants";
|
||||
import {
|
||||
type BuildAbilitiesTupleWithUnknown,
|
||||
type MainWeaponId,
|
||||
type Ability,
|
||||
type AbilityType,
|
||||
type AbilityWithUnknown,
|
||||
abilities,
|
||||
isAbility,
|
||||
|
|
@ -15,11 +13,11 @@ import { buildStats } from "./core/stats";
|
|||
import type { SpecialEffectType } from "./analyzer-types";
|
||||
import {
|
||||
buildToAbilityPoints,
|
||||
serializeBuild,
|
||||
validatedBuildFromSearchParams,
|
||||
validatedWeaponIdFromSearchParams,
|
||||
} from "./core/utils";
|
||||
|
||||
const UNKNOWN_SHORT = "U";
|
||||
|
||||
export function useAnalyzeBuild() {
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
|
||||
|
|
@ -116,64 +114,6 @@ function filterMainOnlyAbilities(
|
|||
return Boolean(abilityObj && abilityObj.type !== "STACKABLE");
|
||||
}
|
||||
|
||||
function serializeBuild(build: BuildAbilitiesTupleWithUnknown) {
|
||||
return build
|
||||
.flat()
|
||||
.map((ability) => (ability === "UNKNOWN" ? UNKNOWN_SHORT : ability))
|
||||
.join(",");
|
||||
}
|
||||
|
||||
function validatedBuildFromSearchParams(
|
||||
searchParams: URLSearchParams,
|
||||
key = "build"
|
||||
): BuildAbilitiesTupleWithUnknown {
|
||||
const abilitiesArr = searchParams.get(key)
|
||||
? searchParams.get(key)?.split(",")
|
||||
: null;
|
||||
|
||||
if (!abilitiesArr) return EMPTY_BUILD;
|
||||
|
||||
try {
|
||||
return [
|
||||
[
|
||||
validateAbility(["STACKABLE", "HEAD_MAIN_ONLY"], abilitiesArr[0]),
|
||||
validateAbility(["STACKABLE"], abilitiesArr[1]),
|
||||
validateAbility(["STACKABLE"], abilitiesArr[2]),
|
||||
validateAbility(["STACKABLE"], abilitiesArr[3]),
|
||||
],
|
||||
[
|
||||
validateAbility(["STACKABLE", "CLOTHES_MAIN_ONLY"], abilitiesArr[4]),
|
||||
validateAbility(["STACKABLE"], abilitiesArr[5]),
|
||||
validateAbility(["STACKABLE"], abilitiesArr[6]),
|
||||
validateAbility(["STACKABLE"], abilitiesArr[7]),
|
||||
],
|
||||
[
|
||||
validateAbility(["STACKABLE", "SHOES_MAIN_ONLY"], abilitiesArr[8]),
|
||||
validateAbility(["STACKABLE"], abilitiesArr[9]),
|
||||
validateAbility(["STACKABLE"], abilitiesArr[10]),
|
||||
validateAbility(["STACKABLE"], abilitiesArr[11]),
|
||||
],
|
||||
];
|
||||
} catch (err) {
|
||||
return EMPTY_BUILD;
|
||||
}
|
||||
}
|
||||
|
||||
function validateAbility(
|
||||
legalTypes: Array<AbilityType>,
|
||||
ability?: string
|
||||
): AbilityWithUnknown {
|
||||
if (!ability) throw new Error("Ability missing");
|
||||
if (ability === UNKNOWN_SHORT) return "UNKNOWN";
|
||||
|
||||
const abilityObj = abilities.find(
|
||||
(a) => a.name === ability && legalTypes.includes(a.type)
|
||||
);
|
||||
if (abilityObj) return abilityObj.name;
|
||||
|
||||
throw new Error("Invalid ability");
|
||||
}
|
||||
|
||||
function validatedLdeIntensityFromSearchParams(searchParams: URLSearchParams) {
|
||||
const ldeIntensity = searchParams.get("lde")
|
||||
? Number(searchParams.get("lde"))
|
||||
|
|
|
|||
|
|
@ -626,7 +626,7 @@ function respawnTime(
|
|||
abilityPoints: args.abilityPoints,
|
||||
ability: QUICK_RESPAWN_TIME_ABILITY,
|
||||
});
|
||||
const abilityPoints = hasRespawnPunisher
|
||||
const abilityPoints = splattedByRP
|
||||
? qrApAfterRespawnPunish({
|
||||
ap,
|
||||
hasTacticooler: args.hasTacticooler,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import type { AbilityType } from "~/modules/in-game-lists";
|
||||
import {
|
||||
abilities,
|
||||
mainWeaponIds,
|
||||
|
|
@ -17,6 +18,8 @@ import type {
|
|||
SubWeaponParams,
|
||||
} from "../analyzer-types";
|
||||
import invariant from "tiny-invariant";
|
||||
import { EMPTY_BUILD } from "~/constants";
|
||||
import { UNKNOWN_SHORT } from "../analyzer-constants";
|
||||
|
||||
export function weaponParams(): ParamsJson {
|
||||
return weaponParamsJson as ParamsJson;
|
||||
|
|
@ -171,6 +174,64 @@ export function validatedWeaponIdFromSearchParams(
|
|||
return weaponCategories[0].weaponIds[0];
|
||||
}
|
||||
|
||||
function validateAbility(
|
||||
legalTypes: Array<AbilityType>,
|
||||
ability?: string
|
||||
): AbilityWithUnknown {
|
||||
if (!ability) throw new Error("Ability missing");
|
||||
if (ability === UNKNOWN_SHORT) return "UNKNOWN";
|
||||
|
||||
const abilityObj = abilities.find(
|
||||
(a) => a.name === ability && legalTypes.includes(a.type)
|
||||
);
|
||||
if (abilityObj) return abilityObj.name;
|
||||
|
||||
throw new Error("Invalid ability");
|
||||
}
|
||||
|
||||
export function validatedBuildFromSearchParams(
|
||||
searchParams: URLSearchParams,
|
||||
key = "build"
|
||||
): BuildAbilitiesTupleWithUnknown {
|
||||
const abilitiesArr = searchParams.get(key)
|
||||
? searchParams.get(key)?.split(",")
|
||||
: null;
|
||||
|
||||
if (!abilitiesArr) return EMPTY_BUILD;
|
||||
|
||||
try {
|
||||
return [
|
||||
[
|
||||
validateAbility(["STACKABLE", "HEAD_MAIN_ONLY"], abilitiesArr[0]),
|
||||
validateAbility(["STACKABLE"], abilitiesArr[1]),
|
||||
validateAbility(["STACKABLE"], abilitiesArr[2]),
|
||||
validateAbility(["STACKABLE"], abilitiesArr[3]),
|
||||
],
|
||||
[
|
||||
validateAbility(["STACKABLE", "CLOTHES_MAIN_ONLY"], abilitiesArr[4]),
|
||||
validateAbility(["STACKABLE"], abilitiesArr[5]),
|
||||
validateAbility(["STACKABLE"], abilitiesArr[6]),
|
||||
validateAbility(["STACKABLE"], abilitiesArr[7]),
|
||||
],
|
||||
[
|
||||
validateAbility(["STACKABLE", "SHOES_MAIN_ONLY"], abilitiesArr[8]),
|
||||
validateAbility(["STACKABLE"], abilitiesArr[9]),
|
||||
validateAbility(["STACKABLE"], abilitiesArr[10]),
|
||||
validateAbility(["STACKABLE"], abilitiesArr[11]),
|
||||
],
|
||||
];
|
||||
} catch (err) {
|
||||
return EMPTY_BUILD;
|
||||
}
|
||||
}
|
||||
|
||||
export function serializeBuild(build: BuildAbilitiesTupleWithUnknown) {
|
||||
return build
|
||||
.flat()
|
||||
.map((ability) => (ability === "UNKNOWN" ? UNKNOWN_SHORT : ability))
|
||||
.join(",");
|
||||
}
|
||||
|
||||
export const hpDivided = (hp: number) => hp / 10;
|
||||
|
||||
export function possibleApValues() {
|
||||
|
|
@ -184,3 +245,6 @@ export function possibleApValues() {
|
|||
|
||||
return Array.from(uniqueValues).sort((a, b) => a - b);
|
||||
}
|
||||
|
||||
export const buildIsEmpty = (build: BuildAbilitiesTupleWithUnknown) =>
|
||||
build.flat().every((ability) => ability === "UNKNOWN");
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
export {
|
||||
possibleApValues,
|
||||
validatedWeaponIdFromSearchParams,
|
||||
validatedBuildFromSearchParams,
|
||||
serializeBuild,
|
||||
hpDivided,
|
||||
} from "./core/utils";
|
||||
export type {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { type LinksFunction, type MetaFunction } from "@remix-run/node";
|
||||
import type { ShouldReloadFunction } from "@remix-run/react";
|
||||
import type { ShouldRevalidateFunction } from "@remix-run/react";
|
||||
import { Link } from "@remix-run/react";
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "~/hooks/useTranslation";
|
||||
|
|
@ -37,6 +37,7 @@ import {
|
|||
objectDamageCalculatorPage,
|
||||
specialWeaponImageUrl,
|
||||
subWeaponImageUrl,
|
||||
userNewBuildPage,
|
||||
} from "~/utils/urls";
|
||||
import clsx from "clsx";
|
||||
import {
|
||||
|
|
@ -59,7 +60,8 @@ import {
|
|||
} from "../analyzer-constants";
|
||||
import { useAnalyzeBuild } from "../analyzer-hooks";
|
||||
import { Tabs, Tab } from "~/components/Tabs";
|
||||
import { isStackableAbility } from "../core/utils";
|
||||
import { buildIsEmpty, isStackableAbility } from "../core/utils";
|
||||
import { useUser } from "~/modules/auth";
|
||||
|
||||
export const CURRENT_PATCH = "2.1";
|
||||
|
||||
|
|
@ -83,9 +85,10 @@ export const handle: SendouRouteHandle = {
|
|||
};
|
||||
|
||||
// Resolves this Github issue: https://github.com/Sendouc/sendou.ink/issues/1053
|
||||
export const unstable_shouldReload: ShouldReloadFunction = () => false;
|
||||
export const shouldRevalidate: ShouldRevalidateFunction = () => false;
|
||||
|
||||
export default function BuildAnalyzerPage() {
|
||||
const user = useUser();
|
||||
const { t } = useTranslation(["analyzer", "common", "weapons"]);
|
||||
useSetTitle(t("common:pages.analyzer"));
|
||||
const {
|
||||
|
|
@ -107,11 +110,9 @@ export default function BuildAnalyzerPage() {
|
|||
return [analyzed.stats[key], analyzed2.stats[key]] as [Stat, Stat];
|
||||
};
|
||||
|
||||
const objectShredderSelected = build[2][0] === "OS";
|
||||
const objectShredderSelected = build[2][0] === "OS" || build2[2][0] === "OS";
|
||||
|
||||
const isComparing =
|
||||
build.flat().some((ability) => ability !== "UNKNOWN") &&
|
||||
build2.flat().some((ability) => ability !== "UNKNOWN");
|
||||
const isComparing = !buildIsEmpty(build) && !buildIsEmpty(build2);
|
||||
|
||||
const mainWeaponCategoryItems = [
|
||||
analyzed.stats.shotSpreadAir && (
|
||||
|
|
@ -736,7 +737,10 @@ export default function BuildAnalyzerPage() {
|
|||
</StatCategory>
|
||||
)}
|
||||
|
||||
<StatCategory title={t("analyzer:stat.category.movement")}>
|
||||
<StatCategory
|
||||
title={t("analyzer:stat.category.movement")}
|
||||
testId="movement-category"
|
||||
>
|
||||
<StatCard
|
||||
isComparing={isComparing}
|
||||
title={t("analyzer:attribute.weight")}
|
||||
|
|
@ -748,6 +752,7 @@ export default function BuildAnalyzerPage() {
|
|||
abilityPoints={abilityPoints}
|
||||
stat={statKeyToTuple("swimSpeed")}
|
||||
title={t("analyzer:stat.swimSpeed")}
|
||||
testId="swim-speed"
|
||||
/>
|
||||
<StatCard
|
||||
isComparing={isComparing}
|
||||
|
|
@ -876,6 +881,24 @@ export default function BuildAnalyzerPage() {
|
|||
{t("analyzer:objCalcAd")}
|
||||
</Link>
|
||||
)}
|
||||
{user && focusedBuild && !buildIsEmpty(focusedBuild) ? (
|
||||
<Link
|
||||
className="analyzer__noticeable-link"
|
||||
to={userNewBuildPage(user, {
|
||||
weapon: mainWeaponId,
|
||||
build: focusedBuild,
|
||||
})}
|
||||
data-testid="new-build-prompt"
|
||||
>
|
||||
<Image
|
||||
path={navIconUrl("builds")}
|
||||
width={24}
|
||||
height={24}
|
||||
alt=""
|
||||
/>
|
||||
{t("analyzer:newBuildPrompt")}
|
||||
</Link>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</Main>
|
||||
|
|
@ -1082,16 +1105,18 @@ function StatCategory({
|
|||
containerClassName = "analyzer__stat-collection",
|
||||
textBelow,
|
||||
summaryRightContent,
|
||||
testId,
|
||||
}: {
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
containerClassName?: string;
|
||||
textBelow?: string;
|
||||
summaryRightContent?: React.ReactNode;
|
||||
testId?: string;
|
||||
}) {
|
||||
return (
|
||||
<details className="analyzer__details">
|
||||
<summary className="analyzer__summary">
|
||||
<summary className="analyzer__summary" data-testid={testId}>
|
||||
{title}
|
||||
{summaryRightContent}
|
||||
</summary>
|
||||
|
|
@ -1110,6 +1135,7 @@ function StatCard({
|
|||
popoverInfo,
|
||||
abilityPoints,
|
||||
isComparing,
|
||||
testId,
|
||||
}: {
|
||||
title: string;
|
||||
stat: [Stat, Stat] | [Stat<string>, Stat<string>] | number | string;
|
||||
|
|
@ -1117,6 +1143,7 @@ function StatCard({
|
|||
popoverInfo?: string;
|
||||
abilityPoints: AbilityPoints;
|
||||
isComparing: boolean;
|
||||
testId?: string;
|
||||
}) {
|
||||
const { t } = useTranslation("analyzer");
|
||||
|
||||
|
|
@ -1177,14 +1204,20 @@ function StatCard({
|
|||
? t("build1")
|
||||
: t("base")}
|
||||
</h4>{" "}
|
||||
<div className="analyzer__stat-card__value__number">
|
||||
<div
|
||||
className="analyzer__stat-card__value__number"
|
||||
data-testid={testId ? `${testId}-base` : undefined}
|
||||
>
|
||||
{showComparison ? (stat as [Stat, Stat])[0].value : baseValue}
|
||||
{suffix}
|
||||
</div>
|
||||
</div>
|
||||
{showBuildValue() ? (
|
||||
<div className="analyzer__stat-card__value">
|
||||
<h4 className="analyzer__stat-card__value__title">
|
||||
<h4
|
||||
className="analyzer__stat-card__value__title"
|
||||
data-testid={testId ? `${testId}-build-title` : undefined}
|
||||
>
|
||||
{showComparison ? t("build2") : t("build")}
|
||||
</h4>{" "}
|
||||
<div className="analyzer__stat-card__value__number">
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import {
|
|||
type ActionFunction,
|
||||
type LoaderArgs,
|
||||
} from "@remix-run/node";
|
||||
import { Form, useLoaderData } from "@remix-run/react";
|
||||
import { Form, useLoaderData, useSearchParams } from "@remix-run/react";
|
||||
import * as React from "react";
|
||||
import { z } from "zod";
|
||||
import { AbilitiesSelector } from "~/components/AbilitiesSelector";
|
||||
|
|
@ -14,9 +14,13 @@ import { Image } from "~/components/Image";
|
|||
import { Label } from "~/components/Label";
|
||||
import { RequiredHiddenInput } from "~/components/RequiredHiddenInput";
|
||||
import { SubmitButton } from "~/components/SubmitButton";
|
||||
import { BUILD, EMPTY_BUILD } from "~/constants";
|
||||
import { BUILD } from "~/constants";
|
||||
import { db } from "~/db";
|
||||
import type { GearType } from "~/db/types";
|
||||
import {
|
||||
validatedBuildFromSearchParams,
|
||||
validatedWeaponIdFromSearchParams,
|
||||
} from "~/features/build-analyzer";
|
||||
import { useTranslation } from "~/hooks/useTranslation";
|
||||
import { requireUser } from "~/modules/auth";
|
||||
import { requireUserId } from "~/modules/auth/user.server";
|
||||
|
|
@ -176,13 +180,16 @@ export const loader = async ({ request }: LoaderArgs) => {
|
|||
Object.fromEntries(url.searchParams)
|
||||
);
|
||||
|
||||
if (!params.success || params.data.userId !== user.id)
|
||||
if (!params.success || params.data.userId !== user.id) {
|
||||
return json({ buildToEdit: null });
|
||||
}
|
||||
|
||||
const usersBuilds = db.builds.buildsByUserId(params.data.userId);
|
||||
const buildToEdit = usersBuilds.find((b) => b.id === params.data.buildId);
|
||||
|
||||
return json({ buildToEdit });
|
||||
return json({
|
||||
buildToEdit,
|
||||
});
|
||||
};
|
||||
|
||||
export default function NewBuildPage() {
|
||||
|
|
@ -298,6 +305,7 @@ function ModeCheckboxes() {
|
|||
}
|
||||
|
||||
function WeaponsSelector() {
|
||||
const [searchParams] = useSearchParams();
|
||||
const { buildToEdit } = useLoaderData<typeof loader>();
|
||||
const { t } = useTranslation(["common", "weapons", "builds"]);
|
||||
const [count, setCount] = React.useState(buildToEdit?.weapons.length ?? 1);
|
||||
|
|
@ -316,7 +324,10 @@ function WeaponsSelector() {
|
|||
inputName="weapon"
|
||||
id="weapon"
|
||||
required
|
||||
initialWeaponId={buildToEdit?.weapons[i]}
|
||||
initialWeaponId={
|
||||
buildToEdit?.weapons[i] ??
|
||||
validatedWeaponIdFromSearchParams(searchParams)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
{i === count - 1 && (
|
||||
|
|
@ -378,10 +389,11 @@ function GearSelector({ type }: { type: GearType }) {
|
|||
}
|
||||
|
||||
function Abilities() {
|
||||
const [searchParams] = useSearchParams();
|
||||
const { buildToEdit } = useLoaderData<typeof loader>();
|
||||
const [abilities, setAbilities] =
|
||||
React.useState<BuildAbilitiesTupleWithUnknown>(
|
||||
buildToEdit?.abilities ?? EMPTY_BUILD
|
||||
buildToEdit?.abilities ?? validatedBuildFromSearchParams(searchParams)
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -991,13 +991,13 @@ dialog::backdrop {
|
|||
}
|
||||
|
||||
.playwire__img {
|
||||
width: 200px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
display: block;
|
||||
width: 200px;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.playwire__text {
|
||||
text-align: center;
|
||||
font-size: var(--fonts-sm);
|
||||
text-align: center;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,11 +14,13 @@ import type {
|
|||
SpecialWeaponId,
|
||||
SubWeaponId,
|
||||
StageId,
|
||||
BuildAbilitiesTupleWithUnknown,
|
||||
} from "~/modules/in-game-lists/types";
|
||||
import type navItems from "~/components/layout/nav-items.json";
|
||||
import { type AuthErrorCode } from "~/modules/auth";
|
||||
import type { StageBackgroundStyle } from "~/features/map-planner";
|
||||
import type { ImageUploadType } from "~/features/img-upload";
|
||||
import { serializeBuild } from "~/features/build-analyzer";
|
||||
|
||||
const staticAssetsUrl = ({
|
||||
folder,
|
||||
|
|
@ -114,8 +116,20 @@ export const userResultsPage = (user: UserLinkArgs) =>
|
|||
`${userPage(user)}/results`;
|
||||
export const userResultsEditHighlightsPage = (user: UserLinkArgs) =>
|
||||
`${userResultsPage(user)}/highlights`;
|
||||
export const userNewBuildPage = (user: UserLinkArgs) =>
|
||||
`${userBuildsPage(user)}/new`;
|
||||
export const userNewBuildPage = (
|
||||
user: UserLinkArgs,
|
||||
params?: { weapon: MainWeaponId; build: BuildAbilitiesTupleWithUnknown }
|
||||
) =>
|
||||
`${userBuildsPage(user)}/new${
|
||||
params
|
||||
? `?${String(
|
||||
new URLSearchParams({
|
||||
weapon: String(params.weapon),
|
||||
build: serializeBuild(params.build),
|
||||
})
|
||||
)}`
|
||||
: ""
|
||||
}`;
|
||||
|
||||
export const teamPage = (customUrl: string) => `/t/${customUrl}`;
|
||||
export const editTeamPage = (customUrl: string) =>
|
||||
|
|
|
|||
49
e2e/analyzer.spec.ts
Normal file
49
e2e/analyzer.spec.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
import {
|
||||
impersonate,
|
||||
navigate,
|
||||
seed,
|
||||
isNotVisible,
|
||||
selectWeapon,
|
||||
} from "~/utils/playwright";
|
||||
import { ANALYZER_URL } from "~/utils/urls";
|
||||
|
||||
test.describe("Build Analyzer", () => {
|
||||
test("analyzes a build and links to new build page with same abilities", async ({
|
||||
page,
|
||||
}) => {
|
||||
await seed(page);
|
||||
await impersonate(page, 1);
|
||||
await navigate({ page, url: ANALYZER_URL });
|
||||
|
||||
const newBuildPrompt = page.getByTestId("new-build-prompt");
|
||||
|
||||
await isNotVisible(newBuildPrompt);
|
||||
|
||||
await selectWeapon({ page, name: "Splattershot" });
|
||||
|
||||
await page.getByTestId("movement-category").click();
|
||||
|
||||
const swimSpeedBase = page.getByTestId("swim-speed-base");
|
||||
const swimSpeedSplattershot = (await swimSpeedBase.textContent())!;
|
||||
|
||||
await selectWeapon({ page, name: "Luna Blaster" });
|
||||
|
||||
// Luna Blaster is a light weapon so it should have lower base swim speed than Splattershot
|
||||
await expect(swimSpeedBase).not.toHaveText(swimSpeedSplattershot);
|
||||
|
||||
// shows comparison value when you have relevant abilities selected
|
||||
const swimSpeedBuildValueTitle = page.getByTestId("swim-speed-build-title");
|
||||
await isNotVisible(swimSpeedBuildValueTitle);
|
||||
await page.getByTestId("SSU-ability-button").click();
|
||||
await swimSpeedBuildValueTitle.isVisible();
|
||||
|
||||
// on new build page with preselected values
|
||||
await newBuildPrompt.click();
|
||||
await expect(page).toHaveURL(/new/);
|
||||
await expect(page.getByTestId("weapon-combobox-input")).toHaveValue(
|
||||
"Luna Blaster"
|
||||
);
|
||||
await page.getByTestId("SSU-ability").isVisible();
|
||||
});
|
||||
});
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
"attribute.weight.Slow": "Heavy",
|
||||
"attribute.weight.Normal": "Normal",
|
||||
"objCalcAd": "For info on Object Shredder check out Object DMG Calc",
|
||||
"newBuildPrompt": "Add a new build with the selected abilities",
|
||||
"stat.category.main": "Main weapon",
|
||||
"stat.category.sub": "Sub weapon",
|
||||
"stat.category.special": "Special weapon",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user