Skip to content

Commit

Permalink
Add a toHavePropertiesOf custom Jasmine matcher.
Browse files Browse the repository at this point in the history
  • Loading branch information
sbruens committed Jan 16, 2024
1 parent d18bfd0 commit 134e0df
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 4 deletions.
1 change: 1 addition & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-namespace": ["error", {"allowDeclarations": true}],
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-unused-vars": [
"warn",
Expand Down
10 changes: 6 additions & 4 deletions src/shadowbox/server/manager_service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {FakePrometheusClient, FakeShadowsocksServer} from './mocks/mocks';
import {AccessKeyConfigJson, ServerAccessKeyRepository} from './server_access_key';
import {ServerConfigJson} from './server_config';
import {SharedMetricsPublisher} from './shared_metrics';
import {customMatchers} from './testing/matchers';

interface ServerInfo {
name: string;
Expand All @@ -50,6 +51,7 @@ describe('ShadowsocksManagerService', () => {
// callback is invoked, followed by the next (done) callback.
let responseProcessed = false;
beforeEach(() => {
jasmine.addMatchers(customMatchers);
responseProcessed = false;
});
afterEach(() => {
Expand Down Expand Up @@ -296,8 +298,8 @@ describe('ShadowsocksManagerService', () => {
expect(data.accessKeys.length).toEqual(2);
const serviceAccessKey1 = data.accessKeys[0];
const serviceAccessKey2 = data.accessKeys[1];
expect(Object.keys(serviceAccessKey1).sort()).toEqual(EXPECTED_ACCESS_KEY_PROPERTIES);
expect(Object.keys(serviceAccessKey2).sort()).toEqual(EXPECTED_ACCESS_KEY_PROPERTIES);
expect(serviceAccessKey1).toHavePropertiesOf(EXPECTED_ACCESS_KEY_PROPERTIES);
expect(serviceAccessKey2).toHavePropertiesOf(EXPECTED_ACCESS_KEY_PROPERTIES);
expect(serviceAccessKey1.name).toEqual(accessKeyName);
responseProcessed = true; // required for afterEach to pass.
},
Expand Down Expand Up @@ -385,7 +387,7 @@ describe('ShadowsocksManagerService', () => {
const res = {
send: (httpCode, data) => {
expect(httpCode).toEqual(201);
expect(Object.keys(data).sort()).toEqual(EXPECTED_ACCESS_KEY_PROPERTIES);
expect(data).toHavePropertiesOf(EXPECTED_ACCESS_KEY_PROPERTIES);
expect(data.method).toEqual('chacha20-ietf-poly1305');
responseProcessed = true; // required for afterEach to pass.
},
Expand All @@ -397,7 +399,7 @@ describe('ShadowsocksManagerService', () => {
const res = {
send: (httpCode, data) => {
expect(httpCode).toEqual(201);
expect(Object.keys(data).sort()).toEqual(EXPECTED_ACCESS_KEY_PROPERTIES);
expect(data).toHavePropertiesOf(EXPECTED_ACCESS_KEY_PROPERTIES);
expect(data.method).toEqual('aes-256-gcm');
responseProcessed = true; // required for afterEach to pass.
},
Expand Down
47 changes: 47 additions & 0 deletions src/shadowbox/server/testing/matchers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright 2024 The Outline Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
* Matchers defined in this module must be declared in this interface for
* TypeScript to be happy.
*/
declare global {
namespace jasmine {
interface Matchers<T> {
toHavePropertiesOf(expected: string[]): void;
}
}
}

/**
* Custom Jasmine matchers.
*/
export const customMatchers: jasmine.CustomMatcherFactories = {
// Compare two objects and returns true if actual contains all the
// properties in the expected.
toHavePropertiesOf: (util: jasmine.MatchersUtil): jasmine.CustomMatcher => {
return {
compare: (actual: Object, expected: string[]): jasmine.CustomMatcherResult => {
const actualProperties = Object.keys(actual);
const isEqual = util.equals(expected.sort(), actualProperties.sort());
return {
pass: isEqual,
message:
`Expected ${jasmine.pp(actual)}${isEqual ? '' : ' not'} ` +
`to contain properties in ${jasmine.pp(expected)}`,
};
},
};
},
};

0 comments on commit 134e0df

Please sign in to comment.