Skip to content

Commit d8ee5c1

Browse files
authored
bug: a workshop map turned on for another mode keeps its name there instead of showing its Workshop ID (#910)
1 parent a7476ca commit d8ee5c1

3 files changed

Lines changed: 163 additions & 1 deletion

File tree

‎components/map-pools/MapPoolRow.vue‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ import { Button } from "~/components/ui/button";
9898
interface Map {
9999
id: string;
100100
name: string;
101+
label?: string | null;
101102
type: string;
102103
patch?: string;
103104
poster?: string;
@@ -173,16 +174,19 @@ export default {
173174
type,
174175
enabled: true,
175176
name: map.name,
177+
label: map.label,
176178
active_pool:
177179
!this.lockedTypes.includes(type) && map.active_pool,
178180
workshop_map_id: map.workshop_map_id,
179181
poster: map.poster,
180182
patch: map.patch,
181183
},
182184
],
185+
// An existing copy takes the label only when there is one, so a
186+
// map without a label never blanks a copy's.
183187
on_conflict: {
184188
constraint: "maps_name_type_key",
185-
update_columns: ["enabled"],
189+
update_columns: map.label ? ["enabled", "label"] : ["enabled"],
186190
},
187191
},
188192
{

‎pages/settings/application/map-pools/index.vue‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,11 @@ export default {
421421
});
422422
}
423423
424+
const unique = uniqueMapsMap.get(map.name)!;
425+
if (!unique.label && map.label) {
426+
unique.label = map.label;
427+
}
428+
424429
if (map.enabled) {
425430
uniqueMapsMap.get(map.name)?.poolTypes.push(map.type);
426431
}
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2+
import { ref } from "vue";
3+
import { flushPromises } from "@vue/test-utils";
4+
import { mountSuspended } from "@nuxt/test-utils/runtime";
5+
import { print } from "graphql";
6+
import type { FieldNode, OperationDefinitionNode } from "graphql";
7+
import MapPoolsPage from "~/pages/settings/application/map-pools/index.vue";
8+
9+
vi.mock("~/components/ui/sidebar/utils", async (original) => ({
10+
...(await original<Record<string, unknown>>()),
11+
useSidebar: () => ({ isMobile: ref(false) }),
12+
}));
13+
14+
// A workshop map's name is its Workshop ID, so a mode row without the label
15+
// shows that number wherever the mode lists the map.
16+
const WORKSHOP_ID = "3199551320";
17+
18+
const row = (type: string, label: string | null, enabled = true) => ({
19+
id: `aim_redline-${type}`,
20+
name: WORKSHOP_ID,
21+
label,
22+
type,
23+
poster: null,
24+
patch: null,
25+
active_pool: false,
26+
workshop_map_id: WORKSHOP_ID,
27+
enabled,
28+
deleted_at: null,
29+
});
30+
31+
const rootField = (query: any) =>
32+
(
33+
(query.definitions[0] as OperationDefinitionNode).selectionSet
34+
.selections[0] as FieldNode
35+
).name.value;
36+
37+
describe("map pools page, workshop map labels", () => {
38+
let push: Record<string, (data: unknown) => void> = {};
39+
let mutations: string[] = [];
40+
let unmount: (() => void) | null = null;
41+
42+
beforeEach(() => {
43+
push = {};
44+
mutations = [];
45+
const client = (useNuxtApp() as any).$apollo.defaultClient;
46+
47+
vi.spyOn(client, "subscribe").mockImplementation((options: any) => ({
48+
subscribe(observer: { next: (value: unknown) => void }) {
49+
push[rootField(options.query)] = (data) => observer.next({ data });
50+
return { closed: false, unsubscribe() {} };
51+
},
52+
}));
53+
54+
vi.spyOn(client, "watchQuery").mockImplementation((options: any) => ({
55+
options,
56+
getCurrentResult: () => ({ loading: true, data: undefined }),
57+
subscribe: () => ({ closed: false, unsubscribe() {} }),
58+
refetch: () => new Promise(() => {}),
59+
setVariables: () => Promise.resolve(),
60+
setOptions: () => Promise.resolve(),
61+
stopPolling() {},
62+
getLastError() {},
63+
getLastResult() {},
64+
resetLastResults() {},
65+
}));
66+
67+
vi.spyOn(client, "mutate").mockImplementation(async (options: any) => {
68+
mutations.push(print(options.mutation));
69+
return { data: {} };
70+
});
71+
});
72+
73+
afterEach(() => {
74+
unmount?.();
75+
unmount = null;
76+
vi.restoreAllMocks();
77+
});
78+
79+
async function mountWith(maps: Array<ReturnType<typeof row>>) {
80+
const wrapper = await mountSuspended(MapPoolsPage, {
81+
global: { mixins: (useNuxtApp().vueApp as any)._context.mixins },
82+
});
83+
unmount = () => wrapper.unmount();
84+
await flushPromises();
85+
push.maps({ maps });
86+
push.map_pools({ map_pools: [] });
87+
await flushPromises();
88+
return wrapper;
89+
}
90+
91+
async function toggleMode(wrapper: any, type: string) {
92+
const toggle = wrapper
93+
.findAll('tbody button[role="switch"]')
94+
.find(
95+
(button: any) =>
96+
button.element.parentElement?.textContent?.trim() === type,
97+
);
98+
expect(toggle, `${type} switch`).toBeDefined();
99+
await toggle.trigger("click");
100+
await flushPromises();
101+
expect(mutations).toHaveLength(1);
102+
return mutations[0];
103+
}
104+
105+
const updateColumns = (mutation: string) =>
106+
(mutation.match(/update_columns:\s*\[([^\]]*)\]/)?.[1] ?? "")
107+
.split(",")
108+
.map((column) => column.trim().replace(/"/g, ""));
109+
110+
it("shows the label when the first row it gets for the map is a copy without one", async () => {
111+
const wrapper = await mountWith([
112+
row("Duel", null),
113+
row("Competitive", "aim_redline"),
114+
]);
115+
116+
const display = wrapper.get("tbody tr td").text();
117+
expect(display).toBe("aim_redline");
118+
});
119+
120+
it("copies the label when the map is turned on for another mode", async () => {
121+
const wrapper = await mountWith([
122+
row("Duel", null),
123+
row("Competitive", "aim_redline"),
124+
]);
125+
126+
const mutation = await toggleMode(wrapper, "Wingman");
127+
128+
expect(mutation).toContain("insert_maps");
129+
expect(mutation).toMatch(/type:\s*Wingman/);
130+
expect(mutation).toMatch(/name:\s*"3199551320"/);
131+
expect(mutation).toMatch(/workshop_map_id:\s*"3199551320"/);
132+
expect(mutation).toMatch(/label:\s*"aim_redline"/);
133+
});
134+
135+
it("gives a mode's old copy the label when that mode is turned back on", async () => {
136+
const wrapper = await mountWith([
137+
row("Competitive", "aim_redline"),
138+
row("Duel", null, false),
139+
]);
140+
141+
const mutation = await toggleMode(wrapper, "Duel");
142+
143+
expect(updateColumns(mutation).sort()).toEqual(["enabled", "label"]);
144+
});
145+
146+
it("never blanks a copy's label from a map that has none", async () => {
147+
const wrapper = await mountWith([row("Competitive", null)]);
148+
149+
const mutation = await toggleMode(wrapper, "Wingman");
150+
151+
expect(updateColumns(mutation)).toEqual(["enabled"]);
152+
});
153+
});

0 commit comments

Comments
 (0)