Add sensible linter rules.

This commit is contained in:
Half-Shot 2024-04-16 22:14:26 +01:00
parent de0953ba1f
commit 6157fbccff
15 changed files with 93 additions and 10 deletions

View File

@ -6,6 +6,7 @@ module.exports = {
],
extends: [
'eslint:recommended',
'plugin:mocha/recommended',
'plugin:@typescript-eslint/recommended',
],
// eslint-config-preact needs a Jest version to be happy, even if Jest isn't used.
@ -18,7 +19,10 @@ module.exports = {
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-unused-vars": "warn",
"camelcase": ["error", { "properties": "never", "ignoreDestructuring": true }],
"no-console": "error"
"no-console": "error",
"mocha/no-exclusive-tests": "error",
// We do this everywhere.
"mocha/no-mocha-arrows": "off",
},
env: {
node: true,

View File

@ -102,7 +102,7 @@
"chai": "^4.3.4",
"eslint": "^8.49.0",
"eslint-config-preact": "^1.3.0",
"eslint-plugin-mocha": "^10.1.0",
"eslint-plugin-mocha": "^10.4.2",
"homerunner-client": "^1.0.0",
"jest": "^29.7.0",
"mini.css": "^3.0.1",

View File

@ -106,6 +106,7 @@ describe("FeedReader", () => {
expect(events[0].data.feed.title).to.equal(null);
expect(events[0].data.title).to.equal(null);
});
it("should handle RSS 2.0 feeds", async () => {
const { events, feedReader, feedUrl } = await constructFeedReader(() => ({
headers: {}, data: `
@ -142,6 +143,7 @@ describe("FeedReader", () => {
expect(events[0].data.link).to.equal('http://www.example.com/blog/post/1');
expect(events[0].data.pubdate).to.equal('Sun, 6 Sep 2009 16:20:00 +0000');
});
it("should handle RSS feeds with a permalink url", async () => {
const { events, feedReader, feedUrl } = await constructFeedReader(() => ({
headers: {}, data: `
@ -177,6 +179,7 @@ describe("FeedReader", () => {
expect(events[0].data.link).to.equal('http://www.example.com/blog/post/1');
expect(events[0].data.pubdate).to.equal('Sun, 6 Sep 2009 16:20:00 +0000');
});
it("should handle Atom feeds", async () => {
const { events, feedReader, feedUrl } = await constructFeedReader(() => ({
headers: {}, data: `
@ -216,6 +219,7 @@ describe("FeedReader", () => {
expect(events[0].data.link).to.equal('http://example.org/2003/12/13/atom03');
expect(events[0].data.pubdate).to.equal('Sat, 13 Dec 2003 18:30:02 +0000');
});
it("should not duplicate feed entries", async () => {
const { events, feedReader, feedUrl } = await constructFeedReader(() => ({
headers: {}, data: `
@ -240,6 +244,7 @@ describe("FeedReader", () => {
await feedReader.pollFeed(feedUrl);
expect(events).to.have.lengthOf(1);
});
it("should always hash to the same value for Atom feeds", async () => {
const expectedHash = ['md5:d41d8cd98f00b204e9800998ecf8427e'];
const { feedReader, feedUrl, storage } = await constructFeedReader(() => ({
@ -259,6 +264,7 @@ describe("FeedReader", () => {
const items = await storage.hasSeenFeedGuids(feedUrl, ...expectedHash);
expect(items).to.deep.equal(expectedHash);
});
it("should always hash to the same value for RSS feeds", async () => {
const expectedHash = [
'md5:98bafde155b931e656ad7c137cd7711e', // guid
@ -292,7 +298,8 @@ describe("FeedReader", () => {
const items = await storage.hasSeenFeedGuids(feedUrl, ...expectedHash);
expect(items).to.deep.equal(expectedHash);
});
it.only("should fail to handle a feed which exceed the maximum size.", async () => {
it("should fail to handle a feed which exceed the maximum size.", async () => {
const { feedReader, feedUrl } = await constructFeedReader(() => ({
headers: {
'Content-Length': Math.pow(1024, 2).toString(),

View File

@ -48,11 +48,13 @@ describe("FormatUtilTest", () => {
"evilcorp/lab: A simple description",
);
});
it("should correctly formats a issue room name", () => {
expect(FormatUtil.formatIssueRoomName(SIMPLE_ISSUE, SIMPLE_REPO)).to.equal(
"evilcorp/lab#123: A simple title",
);
});
it("should correctly generate a partial body for a Github repo", () => {
expect(FormatUtil.getPartialBodyForGithubRepo(SIMPLE_REPO)).to.deep.equal({
"external_url": "https://github.com/evilcorp/lab",
@ -63,6 +65,7 @@ describe("FormatUtilTest", () => {
},
});
});
it("should correctly generate a partial body for a Github issue", () => {
expect(FormatUtil.getPartialBodyForGithubIssue(SIMPLE_REPO, SIMPLE_ISSUE)).to.deep.equal({
"external_url": "https://github.com/evilcorp/lab/issues/123",
@ -79,29 +82,34 @@ describe("FormatUtilTest", () => {
},
});
});
it("should correctly formats a room topic", () => {
expect(FormatUtil.formatRoomTopic(SIMPLE_ISSUE)).to.equal(
"Status: open | https://github.com/evilcorp/lab/issues/123",
);
});
it("should correctly format one simple label", () => {
expect(FormatUtil.formatLabels([{name: "foo"}])).to.deep.equal({
plain: "foo",
html: "<span>foo</span>"
});
});
it("should correctly format many simple labels", () => {
expect(FormatUtil.formatLabels([{name: "foo"},{name: "bar"}])).to.deep.equal({
plain: "foo, bar",
html: "<span>foo</span> <span>bar</span>"
});
});
it("should correctly format one detailed label", () => {
expect(FormatUtil.formatLabels([{name: "foo", color: 'FFFFFF', description: 'My label'}])).to.deep.equal({
plain: "foo",
html: "<span data-mx-bg-color=\"#FFFFFF\" data-mx-color=\"#000000\" title=\"My label\">foo</span>"
});
});
it("should correctly format many detailed labels", () => {
expect(FormatUtil.formatLabels([
{name: "foo", color: 'FFFFFF', description: 'My label'},
@ -112,6 +120,7 @@ describe("FormatUtilTest", () => {
+ "<span data-mx-bg-color=\"#AACCEE\" data-mx-color=\"#000000\" title=\"My other label\">bar</span>"
},);
});
it("should correctly format a JIRA issue", () => {
expect(FormatUtil.getPartialBodyForJiraIssue(SIMPLE_JIRA_ISSUE)).to.deep.equal({
"external_url": "http://example-api.url.com/browse/TEST-001",
@ -127,6 +136,7 @@ describe("FormatUtilTest", () => {
},
});
});
it("should hash an ID", () => {
expect(FormatUtil.hashId("foobar")).to.equal('3858f62230ac3c915f300c664312c63f');
});

View File

@ -6,13 +6,16 @@ const ENABLED_SET = ['enabled-hook', 'enabled-but-ignored'];
describe("HookFilter", () => {
let filter: HookFilter<string>;
beforeEach(() => {
filter = new HookFilter(ENABLED_SET);
});
describe('shouldSkip', () => {
it('should allow a hook named in enabled set', () => {
expect(filter.shouldSkip('enabled-hook')).to.be.false;
});
it('should not allow a hook not named in enabled set', () => {
expect(filter.shouldSkip('not-enabled-hook')).to.be.true;
});

View File

@ -25,6 +25,7 @@ describe("MessageQueueTest", () => {
data: 51,
});
});
it("should be able to push an event, and respond to it", async () => {
mq.subscribe("fakeevent2");
mq.subscribe("response.fakeevent2");

View File

@ -21,43 +21,53 @@ describe("Config/BridgePermissions", () => {
const bridgePermissions = new BridgePermissions([]);
expect(bridgePermissions.checkAction("@foo:bar", "empty-service", "commands")).to.be.false;
});
it("will return false for an insufficent level", () => {
const bridgePermissions = genBridgePermissions('@foo:bar', 'my-service', 'login');
expect(bridgePermissions.checkAction("@foo:bar", "my-service", "notifications")).to.be.false;
});
it("will return false if the there are no matching services", () => {
const bridgePermissions = genBridgePermissions('@foo:bar', 'my-service', 'login');
expect(bridgePermissions.checkAction("@foo:bar", "other-service", "login")).to.be.false;
});
it("will return false if the target does not match", () => {
const bridgePermissions = genBridgePermissions('@foo:bar', 'my-service', 'login');
expect(bridgePermissions.checkAction("@foo:baz", "my-service", "login")).to.be.false;
});
it("will return true if there is a matching level and service", () => {
const bridgePermissions = genBridgePermissions('@foo:bar', 'my-service', 'login');
expect(bridgePermissions.checkAction("@foo:bar", "my-service", "login")).to.be.true;
});
it("will return true for a matching actor domain", () => {
const bridgePermissions = genBridgePermissions('bar', 'my-service', 'login');
expect(bridgePermissions.checkAction("@foo:bar", "my-service", "login")).to.be.true;
});
it("will return true for a wildcard actor", () => {
const bridgePermissions = genBridgePermissions('*', 'my-service', 'login');
expect(bridgePermissions.checkAction("@foo:bar", "my-service", "login")).to.be.true;
});
it("will return true for a wildcard service", () => {
const bridgePermissions = genBridgePermissions('@foo:bar', '*', 'login');
expect(bridgePermissions.checkAction("@foo:bar", "my-service", "login")).to.be.true;
});
it("will return false if a user is not present in a room", () => {
const bridgePermissions = genBridgePermissions('!foo:bar', 'my-service', 'login');
expect(bridgePermissions.checkAction("@foo:bar", "my-service", "login")).to.be.false;
});
it("will return true if a user is present in a room", () => {
const bridgePermissions = genBridgePermissions('!foo:bar', 'my-service', 'login');
bridgePermissions.addMemberToCache('!foo:bar', '@foo:bar');
expect(bridgePermissions.checkAction("@foo:bar", "my-service", "login")).to.be.true;
});
it("will fall through and return true for multiple permission sets", () => {
const bridgePermissions = new BridgePermissions([
{
@ -98,6 +108,7 @@ describe("Config/BridgePermissions", () => {
const bridgePermissions = new BridgePermissions([]);
expect(bridgePermissions.checkActionAny("@foo:bar", "commands")).to.be.false;
});
it(`will return false for a service with an insufficent level`, () => {
const bridgePermissions = genBridgePermissions("@foo:bar", "fake-service", "commands");
expect(

View File

@ -47,6 +47,7 @@ describe("FeedConnection", () => {
expect(matrixEvt.content.external_url).to.equal(FEED_ENTRY_DEFAULTS.link);
expect(matrixEvt.content.body).to.equal("New post in Test feed: [Foo](foo/bar)");
});
it("will handle simple feed message without a title and link ", async () => {
const [connection, intent] = createFeed();
await connection.handleFeedEntry({
@ -60,6 +61,7 @@ describe("FeedConnection", () => {
expect(matrixEvt.content.external_url).to.be.undefined;
expect(matrixEvt.content.body).to.equal("New post in Test feed");
});
it("will handle simple feed message with a missing title ", async () => {
const [connection, intent] = createFeed();
await connection.handleFeedEntry({
@ -71,6 +73,7 @@ describe("FeedConnection", () => {
expect(matrixEvt.roomId).to.equal(ROOM_ID);
expect(matrixEvt.content.body).to.equal("New post in Test feed: [foo/bar](foo/bar)");
});
it("will handle simple feed message with a missing link ", async () => {
const [connection, intent] = createFeed();
await connection.handleFeedEntry({
@ -82,6 +85,7 @@ describe("FeedConnection", () => {
expect(matrixEvt.roomId).to.equal(ROOM_ID);
expect(matrixEvt.content.body).to.equal("New post in Test feed: Foo");
});
it("will handle simple feed message with all the template options possible ", async () => {
const [connection, intent] = createFeed({
template: `$FEEDNAME $FEEDURL $FEEDTITLE $TITLE $LINK $AUTHOR $DATE $SUMMARY`
@ -94,6 +98,7 @@ describe("FeedConnection", () => {
expect(matrixEvt.roomId).to.equal(ROOM_ID);
expect(matrixEvt.content.body).to.equal("Test feed https://example.com/feed.xml Test feed Foo [Foo](foo/bar) Me! today! fibble fobble");
});
it("will handle html in the feed summary ", async () => {
const [connection, intent] = createFeed({
template: `$FEEDNAME $SUMMARY`
@ -107,6 +112,7 @@ describe("FeedConnection", () => {
expect(matrixEvt.roomId).to.equal(ROOM_ID);
expect(matrixEvt.content.body).to.equal('Test feed <p> Some HTML with which should be ignored and an <img src="mxc://fibble/fobble"> </p>');
});
it("will handle partial html in the feed summary ", async () => {
const [connection, intent] = createFeed({
template: `$FEEDNAME $SUMMARY`

View File

@ -65,10 +65,12 @@ describe("GenericHookConnection", () => {
before(async () => {
await GenericHookConnection.initialiseQuickJS();
})
it("will handle simple hook events", async () => {
const [connection, mq] = createGenericHook();
await testSimpleWebhook(connection, mq, "data");
});
it("will handle a hook event containing text", async () => {
const webhookData = {text: "simple-message"};
const [connection, mq] = createGenericHook();
@ -87,6 +89,7 @@ describe("GenericHookConnection", () => {
type: 'm.room.message',
});
});
it("will handle a hook event containing markdown", async () => {
const webhookData = {text: "**bold-message** _italic-message_"};
const [connection, mq] = createGenericHook();
@ -105,6 +108,7 @@ describe("GenericHookConnection", () => {
type: 'm.room.message',
});
});
it("will handle a hook event containing markdown with newlines", async () => {
const webhookData = {text: "# Oh wow\n\n`some-code`"};
const [connection, mq] = createGenericHook();
@ -123,6 +127,7 @@ describe("GenericHookConnection", () => {
type: 'm.room.message',
});
});
it("will handle a hook event containing html", async () => {
const webhookData = {text: "simple-message", html: "<b>simple-message</b>"};
const [connection, mq] = createGenericHook();
@ -141,6 +146,7 @@ describe("GenericHookConnection", () => {
type: 'm.room.message',
});
});
it("will handle a hook event containing a username", async () => {
const webhookData = {username: "Bobs-integration", type: 42};
const [connection, mq] = createGenericHook();
@ -159,6 +165,7 @@ describe("GenericHookConnection", () => {
type: 'm.room.message',
});
});
it("will handle a hook event with a v1 transformation function", async () => {
const webhookData = {question: 'What is the meaning of life?', answer: 42};
const [connection, mq] = createGenericHook({name: 'test', transformationFunction: V1TFFunction}, {
@ -182,6 +189,7 @@ describe("GenericHookConnection", () => {
type: 'm.room.message',
});
});
it("will handle a hook event with a v2 transformation function", async () => {
const webhookData = {question: 'What is the meaning of life?', answer: 42};
const [connection, mq] = createGenericHook({name: 'test', transformationFunction: V2TFFunction}, {
@ -205,6 +213,7 @@ describe("GenericHookConnection", () => {
type: 'm.room.message',
});
});
it("will handle a hook event with a top-level return", async () => {
const webhookData = {question: 'What is the meaning of life?', answer: 42};
const [connection, mq] = createGenericHook({name: 'test', transformationFunction: V2TFFunctionWithReturn}, {
@ -228,6 +237,7 @@ describe("GenericHookConnection", () => {
type: 'm.room.message',
});
});
it("will fail to handle a webhook with an invalid script", async () => {
const webhookData = {question: 'What is the meaning of life?', answer: 42};
const [connection, mq] = createGenericHook({name: 'test', transformationFunction: "bibble bobble"}, {
@ -251,6 +261,7 @@ describe("GenericHookConnection", () => {
type: 'm.room.message',
});
});
it("will handle a message containing floats", async () => {
const [connection, mq] = createGenericHook();
let messagePromise = handleMessage(mq);

View File

@ -86,6 +86,7 @@ describe("GitHubRepoConnection", () => {
}
} as GitHubRepoConnectionState as unknown as Record<string, unknown>);
});
it("will convert ignoredHooks for existing state", () => {
const state = GitHubRepoConnection.validateState({
org: "foo",
@ -96,6 +97,7 @@ describe("GitHubRepoConnection", () => {
} as GitHubRepoConnectionState as unknown as Record<string, unknown>, true);
expect(state.enableHooks).to.not.contain('issue');
});
it("will disallow invalid state", () => {
try {
GitHubRepoConnection.validateState({
@ -108,6 +110,7 @@ describe("GitHubRepoConnection", () => {
}
}
});
it("will disallow enabledHooks to contains invalid enums if this is new state", () => {
try {
GitHubRepoConnection.validateState({
@ -121,6 +124,7 @@ describe("GitHubRepoConnection", () => {
}
}
});
it("will allow enabledHooks to contains invalid enums if this is old state", () => {
GitHubRepoConnection.validateState({
org: "foo",
@ -129,6 +133,7 @@ describe("GitHubRepoConnection", () => {
}, true);
});
});
describe("onIssueCreated", () => {
it("will handle a simple issue", async () => {
const { connection, intent } = createConnection();
@ -138,6 +143,7 @@ describe("GitHubRepoConnection", () => {
intent.expectEventBodyContains(GITHUB_ISSUE_CREATED_PAYLOAD.issue.html_url, 0);
intent.expectEventBodyContains(GITHUB_ISSUE_CREATED_PAYLOAD.issue.title, 0);
});
it("will handle assignees on issue creation", async () => {
const { connection, intent } = createConnection();
await connection.onIssueCreated({
@ -153,6 +159,7 @@ describe("GitHubRepoConnection", () => {
intent.expectEventBodyContains(GITHUB_ISSUE_CREATED_PAYLOAD.issue.html_url, 0);
intent.expectEventBodyContains(GITHUB_ISSUE_CREATED_PAYLOAD.issue.title, 0);
});
it("will filter out issues not matching includingLabels.", async () => {
const { connection, intent } = createConnection({
includingLabels: ["include-me"]
@ -170,6 +177,7 @@ describe("GitHubRepoConnection", () => {
await connection.onIssueCreated(GITHUB_ISSUE_CREATED_PAYLOAD as never);
intent.expectNoEvent();
});
it("will filter out issues matching excludingLabels.", async () => {
const { connection, intent } = createConnection({
excludingLabels: ["exclude-me"]
@ -185,6 +193,7 @@ describe("GitHubRepoConnection", () => {
} as never);
intent.expectNoEvent();
});
it("will include issues matching includingLabels.", async () => {
const { connection, intent } = createConnection({
includingIssues: ["include-me"]

View File

@ -116,6 +116,7 @@ describe("GitLabRepoConnection", () => {
excludingLabels: ["but-not-me"],
} as GitLabRepoConnectionState as unknown as Record<string, unknown>);
});
it("will convert ignoredHooks for existing state", () => {
const state = GitLabRepoConnection.validateState({
instance: "foo",
@ -127,6 +128,7 @@ describe("GitLabRepoConnection", () => {
} as GitLabRepoConnectionState as unknown as Record<string, unknown>, true);
expect(state.enableHooks).to.not.contain('merge_request');
});
it("will disallow invalid state", () => {
try {
GitLabRepoConnection.validateState({
@ -139,6 +141,7 @@ describe("GitLabRepoConnection", () => {
}
}
});
it("will disallow enabledHooks to contains invalid enums if this is new state", () => {
try {
GitLabRepoConnection.validateState({
@ -152,6 +155,7 @@ describe("GitLabRepoConnection", () => {
}
}
});
it("will allow enabledHooks to contains invalid enums if this is old state", () => {
GitLabRepoConnection.validateState({
instance: "bar",
@ -160,6 +164,7 @@ describe("GitLabRepoConnection", () => {
}, true);
});
});
describe("onCommentCreated", () => {
it("will handle an MR comment", async () => {
const { connection, intent } = createConnection();
@ -170,6 +175,7 @@ describe("GitLabRepoConnection", () => {
'event body indicates MR comment'
);
});
it("will debounce MR comments", async () => {
const { connection, intent } = createConnection();
await connection.onCommentCreated(GITLAB_MR_COMMENT as never);
@ -189,6 +195,7 @@ describe("GitLabRepoConnection", () => {
0,
);
});
it("will add new comments in a Matrix thread", async () => {
const { connection, intent } = createConnection();
await connection.onCommentCreated(GITLAB_MR_COMMENT as never);
@ -202,6 +209,7 @@ describe("GitLabRepoConnection", () => {
1,
);
});
it("will correctly map new comments to aggregated discussions", async () => {
const { connection, intent } = createConnection();
await connection.onCommentCreated({
@ -252,6 +260,7 @@ describe("GitLabRepoConnection", () => {
);
});
});
describe("onIssueCreated", () => {
it("will handle a simple issue", async () => {
const { connection, intent } = createConnection();
@ -261,6 +270,7 @@ describe("GitLabRepoConnection", () => {
intent.expectEventBodyContains(GITLAB_ISSUE_CREATED_PAYLOAD.object_attributes.url, 0);
intent.expectEventBodyContains(GITLAB_ISSUE_CREATED_PAYLOAD.object_attributes.title, 0);
});
it("will filter out issues not matching includingLabels.", async () => {
const { connection, intent } = createConnection({
includingLabels: ["include-me"]
@ -275,6 +285,7 @@ describe("GitLabRepoConnection", () => {
await connection.onMergeRequestOpened(GITLAB_ISSUE_CREATED_PAYLOAD as never);
intent.expectNoEvent();
});
it("will filter out issues matching excludingLabels.", async () => {
const { connection, intent } = createConnection({
excludingLabels: ["exclude-me"]
@ -287,6 +298,7 @@ describe("GitLabRepoConnection", () => {
} as never);
intent.expectNoEvent();
});
it("will include issues matching includingLabels.", async () => {
const { connection, intent } = createConnection({
includingIssues: ["include-me"]

View File

@ -13,6 +13,7 @@ describe("GitHub", () => {
})
).equals('https://github.com/login/oauth/authorize?state=my_state&client_id=123&redirect_uri=456');
});
it("can generate an authorize URL for enterprise URLs", () => {
expect(
GithubInstance.generateOAuthUrl(new URL("https://mygithuburl.com/foo/bar"), "authorize", {
@ -22,6 +23,7 @@ describe("GitHub", () => {
})
).equals('https://mygithuburl.com/foo/bar/login/oauth/authorize?state=my_state&client_id=123&redirect_uri=456');
});
it("can generate an access_token URL for the cloud URL", () => {
expect(
GithubInstance.generateOAuthUrl(GITHUB_CLOUD_URL, "access_token", {
@ -33,6 +35,7 @@ describe("GitHub", () => {
})
).equals('https://github.com/login/oauth/access_token?client_id=123&client_secret=the-secret&code=the-code&redirect_uri=456&state=my_state');
});
it("can generate an access_token URL for enterprise URLs", () => {
expect(
GithubInstance.generateOAuthUrl(new URL("https://mygithuburl.com/foo/bar"), "access_token", {

View File

@ -39,6 +39,7 @@ describe("GrantChecker", () => {
let check: GrantChecker<string>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let intent: any;
beforeEach(() => {
intent = IntentMock.create('@foo:bar');
check = new TestGrantChecker(intent, GRANT_SERVICE);
@ -95,9 +96,11 @@ describe("GrantChecker", () => {
);
});
});
describe('config fallback', () => {
let check: GrantChecker<string>;
let as: AppserviceMock;
beforeEach(() => {
const mockAs = AppserviceMock.create();
as = mockAs;

View File

@ -9,13 +9,15 @@ describe("Jira", () => {
key: "TEST-111",
})).to.equal("https://my-test-jira/browse/TEST-111");
});
it("processes a jira issue into a URL with a port", () => {
it("processes a jira issue with a port into a URL", () => {
expect(generateJiraWebLinkFromIssue({
self: "https://my-test-jira:9995/",
key: "TEST-111",
})).to.equal("https://my-test-jira:9995/browse/TEST-111");
});
it("processes a jira issue into a URL with a port", () => {
it("processes a jira issue with a version into a URL", () => {
expect(generateJiraWebLinkFromVersion({
self: "https://my-test-jira:9995/",
description: "foo",

View File

@ -3736,12 +3736,13 @@ eslint-plugin-jest@^25.2.4:
dependencies:
"@typescript-eslint/experimental-utils" "^5.0.0"
eslint-plugin-mocha@^10.1.0:
version "10.2.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-mocha/-/eslint-plugin-mocha-10.2.0.tgz#15b05ce5be4b332bb0d76826ec1c5ebf67102ad6"
integrity sha512-ZhdxzSZnd1P9LqDPF0DBcFLpRIGdh1zkF2JHnQklKQOvrQtT73kdP5K9V2mzvbLR+cCAO9OI48NXK/Ax9/ciCQ==
eslint-plugin-mocha@^10.4.2:
version "10.4.2"
resolved "https://registry.yarnpkg.com/eslint-plugin-mocha/-/eslint-plugin-mocha-10.4.2.tgz#dfaed06d362506c5e4d561c534314e25b3b0f1f7"
integrity sha512-cur4dVYnSEWTBwdqIBQFxa/9siAhesu0TX+lbJ4ClE9j0eNMNe6BSx3vkFFNz6tGoveyMyELFXa30f3fvuAVDg==
dependencies:
eslint-utils "^3.0.0"
globals "^13.24.0"
rambda "^7.4.0"
eslint-plugin-react-hooks@^4.3.0:
@ -4367,7 +4368,7 @@ globals@^11.1.0:
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
globals@^13.19.0:
globals@^13.19.0, globals@^13.24.0:
version "13.24.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171"
integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==