Skip to content

Commit

Permalink
Address all unchecked indexed access within tests
Browse files Browse the repository at this point in the history
  • Loading branch information
louisscruz committed Mar 26, 2023
1 parent 610f3da commit 34eaf2b
Show file tree
Hide file tree
Showing 15 changed files with 151 additions and 114 deletions.
4 changes: 2 additions & 2 deletions src/__testUtils__/dedent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ export function dedent(
strings: ReadonlyArray<string>,
...values: ReadonlyArray<string>
): string {
let str = strings[0];
let str = `${strings[0]}`;

for (let i = 1; i < strings.length; ++i) {
str += values[i - 1] + strings[i]; // interpolation
str += `${values[i - 1]}${strings[i]}`; // interpolation
}
return dedentString(str);
}
8 changes: 8 additions & 0 deletions src/__testUtils__/expectMatchingValues.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,16 @@ import { expectJSON } from './expectJSON.js';

export function expectMatchingValues<T>(values: ReadonlyArray<T>): T {
const [firstValue, ...remainingValues] = values;

/* c8 ignore start */
if (firstValue === undefined) {
throw new Error('Expected a non-empty array');
}
/* c8 ignore stop */

for (const value of remainingValues) {
expectJSON(value).toDeepEqual(firstValue);
}

return firstValue;
}
71 changes: 44 additions & 27 deletions src/__tests__/starWarsData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export interface Character {
appearsIn: ReadonlyArray<number>;
}

export interface Human {
export interface Human extends Character {
type: 'Human';
id: string;
name: string;
Expand All @@ -18,7 +18,7 @@ export interface Human {
homePlanet?: string;
}

export interface Droid {
export interface Droid extends Character {
type: 'Droid';
id: string;
name: string;
Expand All @@ -35,93 +35,110 @@ export interface Droid {
* JSON objects in a more complex demo.
*/

const luke: Human = {
const luke = {
type: 'Human',
id: '1000',
name: 'Luke Skywalker',
friends: ['1002', '1003', '2000', '2001'],
appearsIn: [4, 5, 6],
homePlanet: 'Tatooine',
};
} as const satisfies Human;

const vader: Human = {
const vader = {
type: 'Human',
id: '1001',
name: 'Darth Vader',
friends: ['1004'],
appearsIn: [4, 5, 6],
homePlanet: 'Tatooine',
};
} as const satisfies Human;

const han: Human = {
const han = {
type: 'Human',
id: '1002',
name: 'Han Solo',
friends: ['1000', '1003', '2001'],
appearsIn: [4, 5, 6],
};
} as const satisfies Human;

const leia: Human = {
const leia = {
type: 'Human',
id: '1003',
name: 'Leia Organa',
friends: ['1000', '1002', '2000', '2001'],
appearsIn: [4, 5, 6],
homePlanet: 'Alderaan',
};
} as const satisfies Human;

const tarkin: Human = {
const tarkin = {
type: 'Human',
id: '1004',
name: 'Wilhuff Tarkin',
friends: ['1001'],
appearsIn: [4],
};
} as const satisfies Human;

const humanData: { [id: string]: Human } = {
const humanData = {
[luke.id]: luke,
[vader.id]: vader,
[han.id]: han,
[leia.id]: leia,
[tarkin.id]: tarkin,
};
} as const satisfies { [id: string]: Human };

const threepio: Droid = {
type HumanData = typeof humanData;
type AnyHumanId = keyof HumanData;
type AnyHuman = HumanData[AnyHumanId];

const threepio = {
type: 'Droid',
id: '2000',
name: 'C-3PO',
friends: ['1000', '1002', '1003', '2001'],
appearsIn: [4, 5, 6],
primaryFunction: 'Protocol',
};
} as const satisfies Droid;

const artoo: Droid = {
const artoo = {
type: 'Droid',
id: '2001',
name: 'R2-D2',
friends: ['1000', '1002', '1003'],
appearsIn: [4, 5, 6],
primaryFunction: 'Astromech',
};
} as const satisfies Droid;

const droidData: { [id: string]: Droid } = {
const droidData = {
[threepio.id]: threepio,
[artoo.id]: artoo,
};
} as const satisfies { [id: string]: Droid };

type DroidData = typeof droidData;
type AnyDroidId = keyof DroidData;
type AnyDroid = DroidData[AnyDroidId];

type AnyCharacter = AnyHuman | AnyDroid;
type AnyCharacterId = AnyCharacter['id'];

const isHumanId = (id: AnyCharacterId): id is AnyHumanId =>
Object.hasOwn(humanData, id);

/**
* Helper function to get a character by ID.
*/
function getCharacter(id: string): Promise<Character | null> {
// Returning a promise just to illustrate that GraphQL.js supports it.
return Promise.resolve(humanData[id] ?? droidData[id]);
function getCharacter(id: AnyCharacterId): Promise<AnyCharacter> {
if (isHumanId(id)) {
return Promise.resolve(humanData[id]);
}

return Promise.resolve(droidData[id]);
}

/**
* Allows us to query for a character's friends.
*/
export function getFriends(
character: Character,
export function getFriends<T extends AnyCharacter>(
character: T,
): Array<Promise<Character | null>> {
// Notice that GraphQL accepts Arrays of Promises.
return character.friends.map((id) => getCharacter(id));
Expand All @@ -142,13 +159,13 @@ export function getHero(episode: number): Character {
/**
* Allows us to query for the human with the given id.
*/
export function getHuman(id: string): Human | null {
export function getHuman(id: AnyHumanId): AnyHuman {
return humanData[id];
}

/**
* Allows us to query for the droid with the given id.
*/
export function getDroid(id: string): Droid | null {
export function getDroid(id: AnyDroidId): Droid {
return droidData[id];
}
8 changes: 5 additions & 3 deletions src/error/__tests__/GraphQLError-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const source = new Source(dedent`
`);
const ast = parse(source);
const operationNode = ast.definitions[0];
assert(operationNode.kind === Kind.OPERATION_DEFINITION);
assert(operationNode?.kind === Kind.OPERATION_DEFINITION);
const fieldNode = operationNode.selectionSet.selections[0];
assert(fieldNode != null);

Expand Down Expand Up @@ -247,8 +247,9 @@ describe('toString', () => {
),
);
const opA = docA.definitions[0];
assert(opA.kind === Kind.OBJECT_TYPE_DEFINITION && opA.fields != null);
assert(opA?.kind === Kind.OBJECT_TYPE_DEFINITION && opA.fields != null);
const fieldA = opA.fields[0];
assert(fieldA);

const docB = parse(
new Source(
Expand All @@ -261,8 +262,9 @@ describe('toString', () => {
),
);
const opB = docB.definitions[0];
assert(opB.kind === Kind.OBJECT_TYPE_DEFINITION && opB.fields != null);
assert(opB?.kind === Kind.OBJECT_TYPE_DEFINITION && opB.fields != null);
const fieldB = opB.fields[0];
assert(fieldB);

const error = new GraphQLError('Example error with two nodes', {
nodes: [fieldA.type, fieldB.type],
Expand Down
2 changes: 1 addition & 1 deletion src/execution/__tests__/executor-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ describe('Execute: Handles basic execution tasks', () => {
);

const operation = document.definitions[0];
assert(operation.kind === Kind.OPERATION_DEFINITION);
assert(operation?.kind === Kind.OPERATION_DEFINITION);

expect(resolvedInfo).to.include({
fieldName: 'test',
Expand Down
30 changes: 20 additions & 10 deletions src/execution/__tests__/mapAsyncIterable-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,24 @@ import { mapAsyncIterable } from '../mapAsyncIterable.js';

/* eslint-disable @typescript-eslint/require-await */
describe('mapAsyncIterable', () => {
const doubler = (x: number | undefined) => {
/* c8 ignore start */
if (x === undefined) {
throw new Error('Unexpected undefined in iterator');
}
/* c8 ignore stop */

return x + x;
};

it('maps over async generator', async () => {
async function* source() {
yield 1;
yield 2;
yield 3;
}

const doubles = mapAsyncIterable(source(), (x) => x + x);
const doubles = mapAsyncIterable(source(), doubler);

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

next(): Promise<IteratorResult<number, void>> {
if (items.length > 0) {
const value = items[0];
items.shift();
const value = items.shift();

if (value) {
return Promise.resolve({ done: false, value });
}

return Promise.resolve({ done: true, value: undefined });
},
};

const doubles = mapAsyncIterable(iterable, (x) => x + x);
const doubles = mapAsyncIterable(iterable, doubler);

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

const doubles = mapAsyncIterable(source(), (x) => x + x);
const doubles = mapAsyncIterable(source(), doubler);

const result = [];
for await (const x of doubles) {
Expand Down Expand Up @@ -102,7 +112,7 @@ describe('mapAsyncIterable', () => {
}
}

const doubles = mapAsyncIterable(source(), (x) => x + x);
const doubles = mapAsyncIterable(source(), doubler);

expect(await doubles.next()).to.deep.equal({ value: 2, done: false });
expect(await doubles.next()).to.deep.equal({ value: 4, done: false });
Expand Down Expand Up @@ -141,7 +151,7 @@ describe('mapAsyncIterable', () => {
},
};

const doubles = mapAsyncIterable(iterable, (x) => x + x);
const doubles = mapAsyncIterable(iterable, doubler);

expect(await doubles.next()).to.deep.equal({ value: 2, done: false });
expect(await doubles.next()).to.deep.equal({ value: 4, done: false });
Expand Down Expand Up @@ -205,7 +215,7 @@ describe('mapAsyncIterable', () => {
},
};

const doubles = mapAsyncIterable(iterable, (x) => x + x);
const doubles = mapAsyncIterable(iterable, doubler);

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

const doubles = mapAsyncIterable(source(), (x) => x + x);
const doubles = mapAsyncIterable(source(), doubler);

expect(await doubles.next()).to.deep.equal({ value: 2, done: false });
expect(await doubles.next()).to.deep.equal({ value: 4, done: false });
Expand Down
6 changes: 3 additions & 3 deletions src/execution/__tests__/simplePubSub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ export class SimplePubSub<T> {
return Promise.resolve({ value: undefined, done: true });
}

if (pushQueue.length > 0) {
const value = pushQueue[0];
pushQueue.shift();
const value = pushQueue.shift();
if (value !== undefined) {
return Promise.resolve({ value, done: false });
}

return new Promise((resolve) => pullQueue.push(resolve));
},
return(): Promise<IteratorResult<R, void>> {
Expand Down

0 comments on commit 34eaf2b

Please sign in to comment.