Skip to content

Commit

Permalink
test(type-utils): add basic tests for isTypeReadonly
Browse files Browse the repository at this point in the history
  • Loading branch information
RebeccaStevens committed Jan 9, 2022
1 parent 974858a commit a137c76
Showing 1 changed file with 219 additions and 0 deletions.
219 changes: 219 additions & 0 deletions packages/type-utils/tests/isTypeReadonly.test.ts
@@ -0,0 +1,219 @@
import * as ts from 'typescript';
import { TSESTree } from '@typescript-eslint/experimental-utils';
import { parseForESLint } from '@typescript-eslint/parser';
import {
isTypeReadonly,
type ReadonlynessOptions,
} from '../src/isTypeReadonly';
import path from 'path';

describe('isTypeReadonly', () => {
const rootDir = path.join(__dirname, 'fixtures');

describe('TSTypeAliasDeclaration ', () => {
function getType(code: string): {
type: ts.Type;
checker: ts.TypeChecker;
} {
const { ast, services } = parseForESLint(code, {
project: './tsconfig.json',
filePath: path.join(rootDir, 'file.ts'),
tsconfigRootDir: rootDir,
});
const checker = services.program.getTypeChecker();
const esTreeNodeToTSNodeMap = services.esTreeNodeToTSNodeMap;

const declaration = ast.body[0] as TSESTree.TSTypeAliasDeclaration;
return {
type: checker.getTypeAtLocation(
esTreeNodeToTSNodeMap.get(declaration.id),
),
checker,
};
}

describe('basics', () => {
describe('is readonly', () => {
describe('default options', () => {
it('handles a record with only readonly props', () => {
const { type, checker } = getType(
`type Test = { readonly bar: string; };`,
);

const result = isTypeReadonly(checker, type);
expect(result).toBe(true);
});

it('handles a shallowly mutable record wrapped in Readonly', () => {
const { type: receiver, checker } = getType(
`type Test = Readonly<{ bar: string; }>;`,
);

const result = isTypeReadonly(checker, receiver);
expect(result).toBe(true);
});

it('handles an readonly readonly array', () => {
const { type: receiver, checker } = getType(
`type Test = Readonly<readonly string[]>;`,
);

const result = isTypeReadonly(checker, receiver);
expect(result).toBe(true);
});

it('handles an readonly ReadonlyArray', () => {
const { type: receiver, checker } = getType(
`type Test = Readonly<ReadonlyArray<string>>;`,
);

const result = isTypeReadonly(checker, receiver);
expect(result).toBe(true);
});

it('handles an readonly ReadonlySet', () => {
const { type: receiver, checker } = getType(
`type Test = Readonly<ReadonlySet<string>>;`,
);

const result = isTypeReadonly(checker, receiver);
expect(result).toBe(true);
});

// Methods are mutable but arrays have a special exemption; hence no failure.
it('handles a readonly array', () => {
const { type: receiver, checker } = getType(
`type Test = readonly string[];`,
);

const result = isTypeReadonly(checker, receiver);
expect(result).toBe(true);
});

// Methods are mutable but arrays have a special exemption; hence no failure.
it('handles a ReadonlyArray', () => {
const { type: receiver, checker } = getType(
`type Test = ReadonlyArray<string>;`,
);

const result = isTypeReadonly(checker, receiver);
expect(result).toBe(true);
});
});

describe('treatMethodsAsReadonly', () => {
const options: ReadonlynessOptions = {
treatMethodsAsReadonly: true,
};

it('handles a ReadonlySet', () => {
const { type: receiver, checker } = getType(
`type Test = ReadonlySet<string>;`,
);

const result = isTypeReadonly(checker, receiver, options);
expect(result).toBe(true);
});

it('handles a ReadonlyMap', () => {
const { type: receiver, checker } = getType(
`type Test = ReadonlyMap<string, string>;`,
);

const result = isTypeReadonly(checker, receiver, options);
expect(result).toBe(true);
});
});
});

describe('is not readonly', () => {
it('fails with record with mutable props', () => {
const { type: receiver, checker } = getType(
`type Test = { bar: string; };`,
);

const result = isTypeReadonly(checker, receiver);
expect(result).toBe(false);
});

it('fails with a mutable array', () => {
const { type: receiver, checker } = getType(`type Test = string[];`);

const result = isTypeReadonly(checker, receiver);
expect(result).toBe(false);
});

it('fails with a mutable Array', () => {
const { type: receiver, checker } = getType(
`type Test = Array<string>;`,
);

const result = isTypeReadonly(checker, receiver);
expect(result).toBe(false);
});

// Methods are mutable; hence failure.
it('fails with a ReadonlySet', () => {
const { type: receiver, checker } = getType(
`type Test = ReadonlySet<string>;`,
);

const result = isTypeReadonly(checker, receiver);
expect(result).toBe(false);
});

// Methods are mutable; hence failure.
it('fails with a ReadonlyMap', () => {
const { type: receiver, checker } = getType(
`type Test = ReadonlyMap<string, string>;`,
);

const result = isTypeReadonly(checker, receiver);
expect(result).toBe(false);
});
});
});

describe('Intersection', () => {
describe('is readonly', () => {
it('handles an intersection of 2', () => {
const { type, checker } = getType(
`type Test = Readonly<{ foo: string; bar: number; }> & Readonly<{ bar: number; }>;`,
);

const result = isTypeReadonly(checker, type);
expect(result).toBe(true);
});
});

describe('is not readonly', () => {
it('fails with an intersection of mutables', () => {
const { type, checker } = getType(
`type Test = { foo: string; bar: number; } & { bar: number; };`,
);

const result = isTypeReadonly(checker, type);
expect(result).toBe(false);
});

it('fails with an intersection of mutable to readonly', () => {
const { type, checker } = getType(
`type Test = { foo: string; bar: number; } & Readonly<{ bar: number; }>;`,
);

const result = isTypeReadonly(checker, type);
expect(result).toBe(false);
});

it('fails with an intersection of readonly to mutable', () => {
const { type, checker } = getType(
`type Test = { foo: string; bar: number; } & Readonly<{ bar: number; }>;`,
);

const result = isTypeReadonly(checker, type);
expect(result).toBe(false);
});
});
});
});
});

0 comments on commit a137c76

Please sign in to comment.