feat(common): port utils, tests currently broken

This commit is contained in:
41666 2020-10-11 19:19:12 -04:00
parent c7afd84e1e
commit 5b440ffa8d
10 changed files with 235 additions and 0 deletions

View file

@ -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

View 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",
# ],
# )

View 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);
});

View 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 || <>&nbsp;</>}</div>
))}
</>
);
};

View 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);
});

View 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,
};

View 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);
});

View 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;
};

View 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']);
});

View 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;
});
};