mirror of
https://github.com/roleypoly/roleypoly.git
synced 2025-04-24 19:39:11 +00:00
feat(common): port utils, tests currently broken
This commit is contained in:
parent
c7afd84e1e
commit
5b440ffa8d
10 changed files with 235 additions and 0 deletions
|
@ -11,6 +11,7 @@ def _render_deps(deps = []):
|
|||
if has_added_grpc_deps == False:
|
||||
output_deps.extend([
|
||||
"@npm//google-protobuf",
|
||||
"@npm//@types/google-protobuf",
|
||||
"@npm//@improbable-eng/grpc-web",
|
||||
])
|
||||
has_added_grpc_deps = True
|
||||
|
|
23
src/common/utils/BUILD.bazel
Normal file
23
src/common/utils/BUILD.bazel
Normal file
|
@ -0,0 +1,23 @@
|
|||
load("//:hack/react.bzl", "react_library")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
react_library(
|
||||
name = "utils",
|
||||
deps = [
|
||||
"chroma-js",
|
||||
"react",
|
||||
"styled-components",
|
||||
"//src/rpc/shared",
|
||||
"@types/chroma-js",
|
||||
"@types/react",
|
||||
"@types/styled-components",
|
||||
],
|
||||
)
|
||||
|
||||
# jest_test(
|
||||
# src = ":utils",
|
||||
# deps = [
|
||||
# "//hack/fixtures",
|
||||
# ],
|
||||
# )
|
9
src/common/utils/ReactifyNewlines.spec.tsx
Normal file
9
src/common/utils/ReactifyNewlines.spec.tsx
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { shallow } from 'enzyme';
|
||||
import * as React from 'react';
|
||||
import { ReactifyNewlines } from './ReactifyNewlines';
|
||||
|
||||
it('renders a correct number of divs per newlines', () => {
|
||||
const view = shallow(<ReactifyNewlines>{`1\n2\n3`}</ReactifyNewlines>);
|
||||
|
||||
expect(view.find('div').length).toBe(3);
|
||||
});
|
12
src/common/utils/ReactifyNewlines.tsx
Normal file
12
src/common/utils/ReactifyNewlines.tsx
Normal file
|
@ -0,0 +1,12 @@
|
|||
import * as React from 'react';
|
||||
|
||||
export const ReactifyNewlines = (props: { children: string }) => {
|
||||
const textArray = props.children.split('\n');
|
||||
return (
|
||||
<>
|
||||
{textArray.map((part, idx) => (
|
||||
<div key={`rifynl${idx}`}>{part || <> </>}</div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
37
src/common/utils/hasPermission.spec.ts
Normal file
37
src/common/utils/hasPermission.spec.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import { hasPermission, permissions, hasPermissionOrAdmin } from './hasPermission';
|
||||
import { Role } from 'roleypoly/src/rpc/shared';
|
||||
import { guildRoles } from 'roleypoly/hack/fixtures/storyData';
|
||||
|
||||
const roles: Role.AsObject[] = [
|
||||
{
|
||||
...guildRoles.rolesList[0],
|
||||
permissions: permissions.ADMINISTRATOR,
|
||||
},
|
||||
{
|
||||
...guildRoles.rolesList[0],
|
||||
permissions:
|
||||
permissions.SPEAK | permissions.BAN_MEMBERS | permissions.CHANGE_NICKNAME,
|
||||
},
|
||||
{
|
||||
...guildRoles.rolesList[0],
|
||||
permissions: permissions.BAN_MEMBERS,
|
||||
},
|
||||
];
|
||||
|
||||
it('finds a permission within a list of roles', () => {
|
||||
const result = hasPermission(roles, permissions.CHANGE_NICKNAME);
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('finds admin within a list of roles', () => {
|
||||
const result = hasPermissionOrAdmin(roles, permissions.BAN_MEMBERS);
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('does not find a permission within a list of roles without one', () => {
|
||||
const result = hasPermission(roles, permissions.KICK_MEMBERS);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
45
src/common/utils/hasPermission.ts
Normal file
45
src/common/utils/hasPermission.ts
Normal file
|
@ -0,0 +1,45 @@
|
|||
import { Role } from 'roleypoly/src/rpc/shared';
|
||||
|
||||
export const hasPermission = (roles: Role.AsObject[], permission: number): boolean => {
|
||||
const aggregateRoles = roles.reduce((acc, role) => acc | role.permissions, 0);
|
||||
return (aggregateRoles & permission) === permission;
|
||||
};
|
||||
|
||||
export const hasPermissionOrAdmin = (
|
||||
roles: Role.AsObject[],
|
||||
permission: number
|
||||
): boolean => hasPermission(roles, permission | permissions.ADMINISTRATOR);
|
||||
|
||||
export const permissions = {
|
||||
CREATE_INSTANT_INVITE: 0x1,
|
||||
KICK_MEMBERS: 0x2,
|
||||
BAN_MEMBERS: 0x4,
|
||||
ADMINISTRATOR: 0x8,
|
||||
MANAGE_CHANNELS: 0x10,
|
||||
MANAGE_GUILD: 0x20,
|
||||
ADD_REACTIONS: 0x40,
|
||||
VIEW_AUDIT_LOG: 0x80,
|
||||
VIEW_CHANNEL: 0x400,
|
||||
SEND_MESSAGES: 0x800,
|
||||
SEND_TTS_MESSAGES: 0x1000,
|
||||
MANAGE_MESSAGES: 0x2000,
|
||||
EMBED_LINKS: 0x4000,
|
||||
ATTACH_FILES: 0x8000,
|
||||
READ_MESSAGE_HISTORY: 0x10000,
|
||||
MENTION_EVERYONE: 0x20000,
|
||||
USE_EXTERNAL_EMOJIS: 0x40000,
|
||||
VIEW_GUILD_INSIGHTS: 0x80000,
|
||||
CONNECT: 0x100000,
|
||||
SPEAK: 0x200000,
|
||||
MUTE_MEMBERS: 0x400000,
|
||||
DEAFEN_MEMBERS: 0x800000,
|
||||
MOVE_MEMBERS: 0x1000000,
|
||||
USE_VAD: 0x2000000,
|
||||
PRIORITY_SPEAKER: 0x100,
|
||||
STREAM: 0x200,
|
||||
CHANGE_NICKNAME: 0x4000000,
|
||||
MANAGE_NICKNAMES: 0x8000000,
|
||||
MANAGE_ROLES: 0x10000000,
|
||||
MANAGE_WEBHOOKS: 0x20000000,
|
||||
MANAGE_EMOJIS: 0x40000000,
|
||||
};
|
9
src/common/utils/protoReflection.spec.ts
Normal file
9
src/common/utils/protoReflection.spec.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { DiscordUser } from 'roleypoly/src/rpc/shared';
|
||||
import { user } from 'roleypoly/hack/fixtures/storyData';
|
||||
import { AsObjectToProto } from './protoReflection';
|
||||
|
||||
it('converts a RoleypolyUser.AsObject back to protobuf', () => {
|
||||
const proto = AsObjectToProto(DiscordUser, user);
|
||||
|
||||
expect(proto.toObject()).toMatchObject(user);
|
||||
});
|
30
src/common/utils/protoReflection.ts
Normal file
30
src/common/utils/protoReflection.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import * as pbjs from 'google-protobuf';
|
||||
|
||||
type GenericObject<T extends pbjs.Message> = T;
|
||||
type ProtoFunction<T extends pbjs.Message, U extends ReturnType<T['toObject']>> = (
|
||||
value: U[keyof U]
|
||||
) => void;
|
||||
|
||||
export const AsObjectToProto = <T extends pbjs.Message>(
|
||||
protoClass: { new (): T },
|
||||
input: ReturnType<T['toObject']>
|
||||
): GenericObject<T> => {
|
||||
const proto = new protoClass();
|
||||
const protoKeys = Object.getOwnPropertyNames((proto as any).__proto__);
|
||||
|
||||
for (let inputKey in input) {
|
||||
const setCallName = protoKeys.find(
|
||||
(key) => `set${inputKey.toLowerCase()}` === key.toLowerCase()
|
||||
) as keyof typeof proto;
|
||||
|
||||
if (!setCallName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
((proto[setCallName] as unknown) as ProtoFunction<T, typeof input>)(
|
||||
input[inputKey]
|
||||
);
|
||||
}
|
||||
|
||||
return proto;
|
||||
};
|
48
src/common/utils/sortBy.spec.ts
Normal file
48
src/common/utils/sortBy.spec.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
import { sortBy } from './sortBy';
|
||||
|
||||
it('sorts an array of objects by its key', () => {
|
||||
const output = sortBy(
|
||||
[
|
||||
{
|
||||
name: 'bbb',
|
||||
},
|
||||
{
|
||||
name: 'aaa',
|
||||
},
|
||||
{
|
||||
name: 'ddd',
|
||||
},
|
||||
{
|
||||
name: 'ccc',
|
||||
},
|
||||
],
|
||||
'name'
|
||||
);
|
||||
|
||||
expect(output.map((v) => v.name)).toEqual(['aaa', 'bbb', 'ccc', 'ddd']);
|
||||
});
|
||||
|
||||
it('sorts an array of objects by its key with a predicate', () => {
|
||||
const output = sortBy(
|
||||
[
|
||||
{
|
||||
name: 'cc',
|
||||
},
|
||||
{
|
||||
name: 'bbb',
|
||||
},
|
||||
{
|
||||
name: 'aaaa',
|
||||
},
|
||||
{
|
||||
name: 'd',
|
||||
},
|
||||
],
|
||||
'name',
|
||||
(a, b) => {
|
||||
return a.length > b.length ? 1 : -1;
|
||||
}
|
||||
);
|
||||
|
||||
expect(output.map((v) => v.name)).toEqual(['d', 'cc', 'bbb', 'aaaa']);
|
||||
});
|
21
src/common/utils/sortBy.ts
Normal file
21
src/common/utils/sortBy.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
export const sortBy = <T>(
|
||||
array: T[],
|
||||
key: keyof T,
|
||||
predicate?: (a: T[keyof T], b: T[keyof T]) => number
|
||||
) => {
|
||||
return array.sort((a, b) => {
|
||||
if (predicate) {
|
||||
return predicate(a[key], b[key]);
|
||||
}
|
||||
|
||||
if (a[key] === b[key]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (a[key] > b[key]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
});
|
||||
};
|
Loading…
Add table
Reference in a new issue