Skip to content

Commit 34eaf2b

Browse files
committed
Address all unchecked indexed access within tests
1 parent 610f3da commit 34eaf2b

File tree

15 files changed

+151
-114
lines changed

15 files changed

+151
-114
lines changed

src/__testUtils__/dedent.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ export function dedent(
3232
strings: ReadonlyArray<string>,
3333
...values: ReadonlyArray<string>
3434
): string {
35-
let str = strings[0];
35+
let str = `${strings[0]}`;
3636

3737
for (let i = 1; i < strings.length; ++i) {
38-
str += values[i - 1] + strings[i]; // interpolation
38+
str += `${values[i - 1]}${strings[i]}`; // interpolation
3939
}
4040
return dedentString(str);
4141
}

src/__testUtils__/expectMatchingValues.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,16 @@ import { expectJSON } from './expectJSON.js';
22

33
export function expectMatchingValues<T>(values: ReadonlyArray<T>): T {
44
const [firstValue, ...remainingValues] = values;
5+
6+
/* c8 ignore start */
7+
if (firstValue === undefined) {
8+
throw new Error('Expected a non-empty array');
9+
}
10+
/* c8 ignore stop */
11+
512
for (const value of remainingValues) {
613
expectJSON(value).toDeepEqual(firstValue);
714
}
15+
816
return firstValue;
917
}

src/__tests__/starWarsData.ts

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export interface Character {
99
appearsIn: ReadonlyArray<number>;
1010
}
1111

12-
export interface Human {
12+
export interface Human extends Character {
1313
type: 'Human';
1414
id: string;
1515
name: string;
@@ -18,7 +18,7 @@ export interface Human {
1818
homePlanet?: string;
1919
}
2020

21-
export interface Droid {
21+
export interface Droid extends Character {
2222
type: 'Droid';
2323
id: string;
2424
name: string;
@@ -35,93 +35,110 @@ export interface Droid {
3535
* JSON objects in a more complex demo.
3636
*/
3737

38-
const luke: Human = {
38+
const luke = {
3939
type: 'Human',
4040
id: '1000',
4141
name: 'Luke Skywalker',
4242
friends: ['1002', '1003', '2000', '2001'],
4343
appearsIn: [4, 5, 6],
4444
homePlanet: 'Tatooine',
45-
};
45+
} as const satisfies Human;
4646

47-
const vader: Human = {
47+
const vader = {
4848
type: 'Human',
4949
id: '1001',
5050
name: 'Darth Vader',
5151
friends: ['1004'],
5252
appearsIn: [4, 5, 6],
5353
homePlanet: 'Tatooine',
54-
};
54+
} as const satisfies Human;
5555

56-
const han: Human = {
56+
const han = {
5757
type: 'Human',
5858
id: '1002',
5959
name: 'Han Solo',
6060
friends: ['1000', '1003', '2001'],
6161
appearsIn: [4, 5, 6],
62-
};
62+
} as const satisfies Human;
6363

64-
const leia: Human = {
64+
const leia = {
6565
type: 'Human',
6666
id: '1003',
6767
name: 'Leia Organa',
6868
friends: ['1000', '1002', '2000', '2001'],
6969
appearsIn: [4, 5, 6],
7070
homePlanet: 'Alderaan',
71-
};
71+
} as const satisfies Human;
7272

73-
const tarkin: Human = {
73+
const tarkin = {
7474
type: 'Human',
7575
id: '1004',
7676
name: 'Wilhuff Tarkin',
7777
friends: ['1001'],
7878
appearsIn: [4],
79-
};
79+
} as const satisfies Human;
8080

81-
const humanData: { [id: string]: Human } = {
81+
const humanData = {
8282
[luke.id]: luke,
8383
[vader.id]: vader,
8484
[han.id]: han,
8585
[leia.id]: leia,
8686
[tarkin.id]: tarkin,
87-
};
87+
} as const satisfies { [id: string]: Human };
8888

89-
const threepio: Droid = {
89+
type HumanData = typeof humanData;
90+
type AnyHumanId = keyof HumanData;
91+
type AnyHuman = HumanData[AnyHumanId];
92+
93+
const threepio = {
9094
type: 'Droid',
9195
id: '2000',
9296
name: 'C-3PO',
9397
friends: ['1000', '1002', '1003', '2001'],
9498
appearsIn: [4, 5, 6],
9599
primaryFunction: 'Protocol',
96-
};
100+
} as const satisfies Droid;
97101

98-
const artoo: Droid = {
102+
const artoo = {
99103
type: 'Droid',
100104
id: '2001',
101105
name: 'R2-D2',
102106
friends: ['1000', '1002', '1003'],
103107
appearsIn: [4, 5, 6],
104108
primaryFunction: 'Astromech',
105-
};
109+
} as const satisfies Droid;
106110

107-
const droidData: { [id: string]: Droid } = {
111+
const droidData = {
108112
[threepio.id]: threepio,
109113
[artoo.id]: artoo,
110-
};
114+
} as const satisfies { [id: string]: Droid };
115+
116+
type DroidData = typeof droidData;
117+
type AnyDroidId = keyof DroidData;
118+
type AnyDroid = DroidData[AnyDroidId];
119+
120+
type AnyCharacter = AnyHuman | AnyDroid;
121+
type AnyCharacterId = AnyCharacter['id'];
122+
123+
const isHumanId = (id: AnyCharacterId): id is AnyHumanId =>
124+
Object.hasOwn(humanData, id);
111125

112126
/**
113127
* Helper function to get a character by ID.
114128
*/
115-
function getCharacter(id: string): Promise<Character | null> {
116-
// Returning a promise just to illustrate that GraphQL.js supports it.
117-
return Promise.resolve(humanData[id] ?? droidData[id]);
129+
function getCharacter(id: AnyCharacterId): Promise<AnyCharacter> {
130+
if (isHumanId(id)) {
131+
return Promise.resolve(humanData[id]);
132+
}
133+
134+
return Promise.resolve(droidData[id]);
118135
}
119136

120137
/**
121138
* Allows us to query for a character's friends.
122139
*/
123-
export function getFriends(
124-
character: Character,
140+
export function getFriends<T extends AnyCharacter>(
141+
character: T,
125142
): Array<Promise<Character | null>> {
126143
// Notice that GraphQL accepts Arrays of Promises.
127144
return character.friends.map((id) => getCharacter(id));
@@ -142,13 +159,13 @@ export function getHero(episode: number): Character {
142159
/**
143160
* Allows us to query for the human with the given id.
144161
*/
145-
export function getHuman(id: string): Human | null {
162+
export function getHuman(id: AnyHumanId): AnyHuman {
146163
return humanData[id];
147164
}
148165

149166
/**
150167
* Allows us to query for the droid with the given id.
151168
*/
152-
export function getDroid(id: string): Droid | null {
169+
export function getDroid(id: AnyDroidId): Droid {
153170
return droidData[id];
154171
}

src/error/__tests__/GraphQLError-test.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const source = new Source(dedent`
1616
`);
1717
const ast = parse(source);
1818
const operationNode = ast.definitions[0];
19-
assert(operationNode.kind === Kind.OPERATION_DEFINITION);
19+
assert(operationNode?.kind === Kind.OPERATION_DEFINITION);
2020
const fieldNode = operationNode.selectionSet.selections[0];
2121
assert(fieldNode != null);
2222

@@ -247,8 +247,9 @@ describe('toString', () => {
247247
),
248248
);
249249
const opA = docA.definitions[0];
250-
assert(opA.kind === Kind.OBJECT_TYPE_DEFINITION && opA.fields != null);
250+
assert(opA?.kind === Kind.OBJECT_TYPE_DEFINITION && opA.fields != null);
251251
const fieldA = opA.fields[0];
252+
assert(fieldA);
252253

253254
const docB = parse(
254255
new Source(
@@ -261,8 +262,9 @@ describe('toString', () => {
261262
),
262263
);
263264
const opB = docB.definitions[0];
264-
assert(opB.kind === Kind.OBJECT_TYPE_DEFINITION && opB.fields != null);
265+
assert(opB?.kind === Kind.OBJECT_TYPE_DEFINITION && opB.fields != null);
265266
const fieldB = opB.fields[0];
267+
assert(fieldB);
266268

267269
const error = new GraphQLError('Example error with two nodes', {
268270
nodes: [fieldA.type, fieldB.type],

src/execution/__tests__/executor-test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ describe('Execute: Handles basic execution tasks', () => {
225225
);
226226

227227
const operation = document.definitions[0];
228-
assert(operation.kind === Kind.OPERATION_DEFINITION);
228+
assert(operation?.kind === Kind.OPERATION_DEFINITION);
229229

230230
expect(resolvedInfo).to.include({
231231
fieldName: 'test',

src/execution/__tests__/mapAsyncIterable-test.ts

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,24 @@ import { mapAsyncIterable } from '../mapAsyncIterable.js';
77

88
/* eslint-disable @typescript-eslint/require-await */
99
describe('mapAsyncIterable', () => {
10+
const doubler = (x: number | undefined) => {
11+
/* c8 ignore start */
12+
if (x === undefined) {
13+
throw new Error('Unexpected undefined in iterator');
14+
}
15+
/* c8 ignore stop */
16+
17+
return x + x;
18+
};
19+
1020
it('maps over async generator', async () => {
1121
async function* source() {
1222
yield 1;
1323
yield 2;
1424
yield 3;
1525
}
1626

17-
const doubles = mapAsyncIterable(source(), (x) => x + x);
27+
const doubles = mapAsyncIterable(source(), doubler);
1828

1929
expect(await doubles.next()).to.deep.equal({ value: 2, done: false });
2030
expect(await doubles.next()).to.deep.equal({ value: 4, done: false });
@@ -34,17 +44,17 @@ describe('mapAsyncIterable', () => {
3444
},
3545

3646
next(): Promise<IteratorResult<number, void>> {
37-
if (items.length > 0) {
38-
const value = items[0];
39-
items.shift();
47+
const value = items.shift();
48+
49+
if (value) {
4050
return Promise.resolve({ done: false, value });
4151
}
4252

4353
return Promise.resolve({ done: true, value: undefined });
4454
},
4555
};
4656

47-
const doubles = mapAsyncIterable(iterable, (x) => x + x);
57+
const doubles = mapAsyncIterable(iterable, doubler);
4858

4959
expect(await doubles.next()).to.deep.equal({ value: 2, done: false });
5060
expect(await doubles.next()).to.deep.equal({ value: 4, done: false });
@@ -62,7 +72,7 @@ describe('mapAsyncIterable', () => {
6272
yield 3;
6373
}
6474

65-
const doubles = mapAsyncIterable(source(), (x) => x + x);
75+
const doubles = mapAsyncIterable(source(), doubler);
6676

6777
const result = [];
6878
for await (const x of doubles) {
@@ -102,7 +112,7 @@ describe('mapAsyncIterable', () => {
102112
}
103113
}
104114

105-
const doubles = mapAsyncIterable(source(), (x) => x + x);
115+
const doubles = mapAsyncIterable(source(), doubler);
106116

107117
expect(await doubles.next()).to.deep.equal({ value: 2, done: false });
108118
expect(await doubles.next()).to.deep.equal({ value: 4, done: false });
@@ -141,7 +151,7 @@ describe('mapAsyncIterable', () => {
141151
},
142152
};
143153

144-
const doubles = mapAsyncIterable(iterable, (x) => x + x);
154+
const doubles = mapAsyncIterable(iterable, doubler);
145155

146156
expect(await doubles.next()).to.deep.equal({ value: 2, done: false });
147157
expect(await doubles.next()).to.deep.equal({ value: 4, done: false });
@@ -205,7 +215,7 @@ describe('mapAsyncIterable', () => {
205215
},
206216
};
207217

208-
const doubles = mapAsyncIterable(iterable, (x) => x + x);
218+
const doubles = mapAsyncIterable(iterable, doubler);
209219

210220
expect(await doubles.next()).to.deep.equal({ value: 2, done: false });
211221
expect(await doubles.next()).to.deep.equal({ value: 4, done: false });
@@ -228,7 +238,7 @@ describe('mapAsyncIterable', () => {
228238
}
229239
}
230240

231-
const doubles = mapAsyncIterable(source(), (x) => x + x);
241+
const doubles = mapAsyncIterable(source(), doubler);
232242

233243
expect(await doubles.next()).to.deep.equal({ value: 2, done: false });
234244
expect(await doubles.next()).to.deep.equal({ value: 4, done: false });

src/execution/__tests__/simplePubSub.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@ export class SimplePubSub<T> {
4040
return Promise.resolve({ value: undefined, done: true });
4141
}
4242

43-
if (pushQueue.length > 0) {
44-
const value = pushQueue[0];
45-
pushQueue.shift();
43+
const value = pushQueue.shift();
44+
if (value !== undefined) {
4645
return Promise.resolve({ value, done: false });
4746
}
47+
4848
return new Promise((resolve) => pullQueue.push(resolve));
4949
},
5050
return(): Promise<IteratorResult<R, void>> {

0 commit comments

Comments
 (0)