diff --git a/app/javascript/chat/__tests__/__snapshots__/alert.test.jsx.snap b/app/javascript/chat/__tests__/__snapshots__/alert.test.jsx.snap
new file mode 100644
index 000000000..8d2b7b896
--- /dev/null
+++ b/app/javascript/chat/__tests__/__snapshots__/alert.test.jsx.snap
@@ -0,0 +1,17 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` should render and test snapshot 1`] = `
+
+ More new messages below
+
+`;
+
+exports[` should render and test snapshot with hidden class 1`] = `
+
+ More new messages below
+
+`;
diff --git a/app/javascript/chat/__tests__/__snapshots__/codeEditor.test.jsx.snap b/app/javascript/chat/__tests__/__snapshots__/codeEditor.test.jsx.snap
new file mode 100644
index 000000000..2172d92b2
--- /dev/null
+++ b/app/javascript/chat/__tests__/__snapshots__/codeEditor.test.jsx.snap
@@ -0,0 +1,14 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` should render and test snapshot 1`] = `
+
+
+
+`;
diff --git a/app/javascript/chat/__tests__/__snapshots__/compose.test.jsx.snap b/app/javascript/chat/__tests__/__snapshots__/compose.test.jsx.snap
new file mode 100644
index 000000000..c258418fa
--- /dev/null
+++ b/app/javascript/chat/__tests__/__snapshots__/compose.test.jsx.snap
@@ -0,0 +1,21 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` behavior with message should render and test snapshot 1`] = `
+
+
+
+
+`;
diff --git a/app/javascript/chat/__tests__/__snapshots__/githubRepo.test.jsx.snap b/app/javascript/chat/__tests__/__snapshots__/githubRepo.test.jsx.snap
new file mode 100644
index 000000000..25ff078be
--- /dev/null
+++ b/app/javascript/chat/__tests__/__snapshots__/githubRepo.test.jsx.snap
@@ -0,0 +1,61 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` with fake token should have the proper elements, attributes and values with states set 1`] = `
+RenderContext {
+ "0": VNode {
+ "attributes": Object {
+ "activeChannelId": 12345,
+ "githubToken": "some_token",
+ "pusherKey": "ASDFGHJKL",
+ "resource": Object {
+ "args": "someargs",
+ },
+ },
+ "children": Array [],
+ "key": undefined,
+ "nodeName": [Function],
+ },
+ "length": 1,
+}
+`;
+
+exports[` with fake token should render and test snapshot 1`] = `
+
+`;
+
+exports[` with no token should render and test snapshot 1`] = `
+
+
+
+ You must
+
+ authenticate with GitHub
+
+ to use this feature.
+
+
+`;
diff --git a/app/javascript/chat/__tests__/__snapshots__/message.test.jsx.snap b/app/javascript/chat/__tests__/__snapshots__/message.test.jsx.snap
new file mode 100644
index 000000000..8a827ac44
--- /dev/null
+++ b/app/javascript/chat/__tests__/__snapshots__/message.test.jsx.snap
@@ -0,0 +1,63 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` should render and test snapshot 1`] = `
+
+`;
diff --git a/app/javascript/chat/__tests__/__snapshots__/userDetails.test.jsx.snap b/app/javascript/chat/__tests__/__snapshots__/userDetails.test.jsx.snap
new file mode 100644
index 000000000..44017a891
--- /dev/null
+++ b/app/javascript/chat/__tests__/__snapshots__/userDetails.test.jsx.snap
@@ -0,0 +1,237 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` for user1 should render and test snapshot 1`] = `
+
+

+
+
+
+ I am the Bojack Horseman from Horsing Around and Secreteriat
+
+
+
+
+ location
+
+
+ Los Angeles, CA
+
+
+
+ joined
+
+
+ January 2, 1964
+
+
+
+`;
+
+exports[` for user2 should render and test snapshot 1`] = `
+
+

+
+
+
+ Woof Woof *smile*
+
+
+
+
+ location
+
+
+ Los Angeles, DO(G)
+
+
+
+ joined
+
+
+ 1960s
+
+
+
+`;
diff --git a/app/javascript/chat/__tests__/__snapshots__/view.test.jsx.snap b/app/javascript/chat/__tests__/__snapshots__/view.test.jsx.snap
new file mode 100644
index 000000000..b4b5d132e
--- /dev/null
+++ b/app/javascript/chat/__tests__/__snapshots__/view.test.jsx.snap
@@ -0,0 +1,45 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` should render and test snapshot (no channel) 1`] = `
+
+
+
+
+ Channel Invitations 🤗
+
+
+
+`;
+
+exports[` should render and test snapshot (with channel) 1`] = `
+RenderContext {
+ "0": VNode {
+ "attributes": Object {
+ "channels": Array [
+ Object {
+ "channel_name": "name",
+ "description": "some description",
+ "membership_id": "12345",
+ },
+ ],
+ "onAcceptInvitation": [Function],
+ "onDeclineInvitation": [Function],
+ "onViewExit": [Function],
+ },
+ "children": Array [],
+ "key": undefined,
+ "nodeName": [Function],
+ },
+ "length": 1,
+}
+`;
diff --git a/app/javascript/chat/__tests__/alert.test.jsx b/app/javascript/chat/__tests__/alert.test.jsx
new file mode 100644
index 000000000..4e69f4a38
--- /dev/null
+++ b/app/javascript/chat/__tests__/alert.test.jsx
@@ -0,0 +1,34 @@
+import { h } from 'preact';
+import render from 'preact-render-to-json';
+import { shallow } from 'preact-render-spy';
+import Alert from '../alert';
+
+describe('', () => {
+ it('should render and test snapshot', () => {
+ const tree = render();
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('should render and test snapshot with hidden class', () => {
+ const tree = render();
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('should have div without hidden class', () => {
+ const context = shallow();
+ expect(context.find('.chatalert__default').exists()).toEqual(true);
+ expect(context.find('.chatalert__default').text()).toEqual(
+ 'More new messages below',
+ );
+ expect(context.find('.chatalert__default--hidden').exists()).toEqual(false);
+ });
+
+ it('should have div with hidden class', () => {
+ const context = shallow();
+ expect(context.find('.chatalert__default').exists()).toEqual(true);
+ expect(context.find('.chatalert__default').text()).toEqual(
+ 'More new messages below',
+ );
+ expect(context.find('.chatalert__default--hidden').exists()).toEqual(true);
+ });
+});
diff --git a/app/javascript/chat/__tests__/codeEditor.test.jsx b/app/javascript/chat/__tests__/codeEditor.test.jsx
new file mode 100644
index 000000000..8215d2c2a
--- /dev/null
+++ b/app/javascript/chat/__tests__/codeEditor.test.jsx
@@ -0,0 +1,22 @@
+import { h } from 'preact';
+import render from 'preact-render-to-json';
+// import { shallow } from 'preact-render-spy';
+import CodeEditor from '../codeEditor';
+
+const getCodeEditor = () => (
+
+);
+
+describe('', () => {
+ it('should render and test snapshot', () => {
+ const tree = render(getCodeEditor());
+ expect(tree).toMatchSnapshot();
+ });
+
+ // it('should have the proper attributes and text values', () => {
+ // const context = shallow(getCodeEditor());
+ // expect(context.find('codeeditor').exists()).toEqual(true);
+ // expect(context.find('.chatcodeeditor__header').exists()).toEqual(true);
+ // expect(context.find('.chatcodeeditor__header').text()).toEqual('Experimental (WIP)');
+ // });
+});
diff --git a/app/javascript/chat/__tests__/compose.test.jsx b/app/javascript/chat/__tests__/compose.test.jsx
new file mode 100644
index 000000000..7af88477b
--- /dev/null
+++ b/app/javascript/chat/__tests__/compose.test.jsx
@@ -0,0 +1,137 @@
+import { h } from 'preact';
+import render from 'preact-render-to-json';
+import { shallow } from 'preact-render-spy';
+import Compose from '../compose';
+
+let submitNoMessage = false;
+let submitWithMessage = false;
+let textfieldIsEmpty = true;
+
+const handleSubmitEmpty = () => {
+ submitNoMessage = true;
+ submitWithMessage = false;
+ textfieldIsEmpty = true;
+};
+
+const handleSubmitFake = () => {
+ submitNoMessage = false;
+ submitWithMessage = true;
+ textfieldIsEmpty = true;
+};
+
+const handleKeyDownFake = e => {
+ const enterPressed = e.keyCode === 13;
+ if (!enterPressed) {
+ textfieldIsEmpty = false;
+ } else if (textfieldIsEmpty) {
+ handleSubmitEmpty();
+ } else {
+ handleSubmitFake();
+ }
+};
+
+const getCompose = tf => {
+ // true -> not empty, false -> empty
+ if (tf) {
+ return (
+
+ );
+ }
+ return (
+
+ );
+};
+
+describe('', () => {
+ afterEach(() => {
+ submitNoMessage = false;
+ submitWithMessage = false;
+ textfieldIsEmpty = true;
+ });
+
+ describe('behavior with no message', () => {
+ it('should click submit', () => {
+ const context = shallow(getCompose(false));
+ const btn = context.find('.messagecomposer__submit');
+
+ expect(btn.simulate('click'));
+
+ expect(submitNoMessage).toEqual(true);
+ expect(submitWithMessage).toEqual(false);
+ expect(textfieldIsEmpty).toEqual(true);
+ });
+
+ it('should press enter', () => {
+ const context = shallow(getCompose(false));
+ const input = context.find('.messagecomposer__input');
+
+ const enter = { keyCode: 13 };
+ expect(input.simulate('keyDown', enter));
+
+ expect(submitNoMessage).toEqual(true);
+ expect(submitWithMessage).toEqual(false);
+ expect(textfieldIsEmpty).toEqual(true);
+ });
+ });
+
+ describe('behavior with message', () => {
+ it('should render and test snapshot', () => {
+ const tree = render(getCompose(true));
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('should have proper elements, attributes and values', () => {
+ const context = shallow(getCompose(true));
+ expect(context.find('.messagecomposer').exists()).toEqual(true);
+
+ const input = context.find('.messagecomposer__input');
+ expect(input.exists()).toEqual(true);
+ expect(input.text()).toEqual('');
+ expect(input.attr('maxLength')).toEqual('1000');
+ expect(input.attr('placeholder')).toEqual('Message goes here');
+
+ const btn = context.find('.messagecomposer__submit');
+ expect(btn.exists()).toEqual(true);
+ expect(btn.text()).toEqual('SEND');
+ });
+
+ it('should click submit and check for empty textarea', () => {
+ const context = shallow(getCompose(true));
+ const input = context.find('.messagecomposer__input');
+ const btn = context.find('.messagecomposer__submit');
+
+ const someletter = { keyCode: 69 };
+
+ expect(input.simulate('keyDown', someletter));
+ expect(textfieldIsEmpty).toEqual(false);
+
+ expect(btn.simulate('click'));
+
+ expect(submitNoMessage).toEqual(false);
+ expect(submitWithMessage).toEqual(true);
+ expect(textfieldIsEmpty).toEqual(true);
+ });
+
+ it('should press enter and check for empty textarea', () => {
+ const context = shallow(getCompose(true));
+ const input = context.find('.messagecomposer__input');
+
+ const someletter = { keyCode: 69 };
+ expect(input.simulate('keyDown', someletter));
+ const enter = { keyCode: 13 };
+ expect(input.simulate('keyDown', enter));
+
+ expect(submitNoMessage).toEqual(false);
+ expect(submitWithMessage).toEqual(true);
+ expect(textfieldIsEmpty).toEqual(true);
+ });
+ });
+});
diff --git a/app/javascript/chat/__tests__/githubRepo.test.jsx b/app/javascript/chat/__tests__/githubRepo.test.jsx
new file mode 100644
index 000000000..f697212b8
--- /dev/null
+++ b/app/javascript/chat/__tests__/githubRepo.test.jsx
@@ -0,0 +1,191 @@
+import { h } from 'preact';
+import render from 'preact-render-to-json';
+import { shallow } from 'preact-render-spy';
+import fetch from 'jest-fetch-mock';
+import marked from 'marked';
+import GithubRepo from '../githubRepo';
+
+global.fetch = fetch;
+
+const getGithubRepo = token => (
+
+);
+
+describe('', () => {
+ describe('with no token', () => {
+ it('should render and test snapshot', () => {
+ const tree = render(getGithubRepo());
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('should have the proper elements, attributes and values', () => {
+ const context = shallow(getGithubRepo());
+ expect(context.find('.activecontent__githubrepo').exists()).toEqual(true);
+ expect(context.find('em').text()).toEqual('Authentication required');
+ });
+ });
+
+ describe('with fake token', () => {
+ it('should render and test snapshot', () => {
+ const tree = render(getGithubRepo('some_token'));
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('should have the proper elements, attributes and values with states set', async () => {
+ const context = await shallow(getGithubRepo('some_token'));
+ const dirs = [
+ {
+ name: 'Camera',
+ path: 'Camera',
+ sha: 'hysst5jI2idHutihWo3JxYlTByoj0lkdXmkmuBEp',
+ size: 0,
+ url:
+ 'https://api.github.com/repos/username/repositoryname/contents/Camera?ref=master',
+ html_url:
+ 'https://github.com/username/repositoryname/tree/master/Camera',
+ git_url:
+ 'https://api.github.com/repos/username/repositoryname/git/trees/hysst5jI2idHutihWo3JxYlTByoj0lkdXmkmuBEp',
+ download_url: null,
+ type: 'dir',
+ _links: {
+ self:
+ 'https://api.github.com/repos/username/repositoryname/contents/Camera?ref=master',
+ git:
+ 'https://api.github.com/repos/username/repositoryname/git/trees/hysst5jI2idHutihWo3JxYlTByoj0lkdXmkmuBEp',
+ html:
+ 'https://github.com/username/repositoryname/tree/master/Camera',
+ },
+ },
+ {
+ name: 'Environment',
+ path: 'Environment',
+ sha: 'dwogYlYGQOXj1ru3L9HYfX7HdX3WNQPJgJVeStRs',
+ size: 0,
+ url:
+ 'https://api.github.com/repos/username/repositoryname/contents/Environment?ref=master',
+ html_url:
+ 'https://github.com/username/repositoryname/tree/master/Environment',
+ git_url:
+ 'https://api.github.com/repos/username/repositoryname/git/trees/dwogYlYGQOXj1ru3L9HYfX7HdX3WNQPJgJVeStRs',
+ download_url: null,
+ type: 'dir',
+ _links: {
+ self:
+ 'https://api.github.com/repos/username/repositoryname/contents/Environment?ref=master',
+ git:
+ 'https://api.github.com/repos/username/repositoryname/git/trees/dwogYlYGQOXj1ru3L9HYfX7HdX3WNQPJgJVeStRs',
+ html:
+ 'https://github.com/username/repositoryname/tree/master/Environment',
+ },
+ },
+ {
+ name: 'Interactables',
+ path: 'Interactables',
+ sha: '44OLxtYSQjr2DLVKPnwGTj6JuQpo7Te7pEIDULat',
+ size: 0,
+ url:
+ 'https://api.github.com/repos/username/repositoryname/contents/Interactables?ref=master',
+ html_url:
+ 'https://github.com/username/repositoryname/tree/master/Interactables',
+ git_url:
+ 'https://api.github.com/repos/username/repositoryname/git/trees/44OLxtYSQjr2DLVKPnwGTj6JuQpo7Te7pEIDULat',
+ download_url: null,
+ type: 'dir',
+ _links: {
+ self:
+ 'https://api.github.com/repos/username/repositoryname/contents/Interactables?ref=master',
+ git:
+ 'https://api.github.com/repos/username/repositoryname/git/trees/44OLxtYSQjr2DLVKPnwGTj6JuQpo7Te7pEIDULat',
+ html:
+ 'https://github.com/username/repositoryname/tree/master/Interactables',
+ },
+ },
+ ];
+ context.setState({ directories: dirs });
+ const fils = [
+ {
+ name: 'Level Design',
+ path: 'Level Design',
+ sha: 'g178Oy2xV8gqFBhoaflJkSbHN01dxWEqjJTxw6Ax',
+ size: 583,
+ url:
+ 'https://api.github.com/repos/username/repositoryname/contents/Level%20Design?ref=master',
+ html_url:
+ 'https://github.com/username/repositoryname/tree/master/Level%20Design',
+ git_url:
+ 'https://api.github.com/repos/username/repositoryname/git/trees/g178Oy2xV8gqFBhoaflJkSbHN01dxWEqjJTxw6Ax',
+ download_url:
+ 'https://raw.githubusercontent.com/username/repositoryname/master/Level%20Design',
+ type: 'file',
+ _links: {
+ self:
+ 'https://api.github.com/repos/username/repositoryname/contents/Level%20Design?ref=master',
+ git:
+ 'https://api.github.com/repos/username/repositoryname/git/trees/g178Oy2xV8gqFBhoaflJkSbHN01dxWEqjJTxw6Ax',
+ html:
+ 'https://github.com/username/repositoryname/tree/master/Level%20Design',
+ },
+ },
+ {
+ name: 'Player',
+ path: 'Player',
+ sha: 'tia6ISq19krmOJzbtwpCTIuwRnvpSxZY2g1FMgOp',
+ size: 1000,
+ url:
+ 'https://api.github.com/repos/username/repositoryname/contents/Player?ref=master',
+ html_url:
+ 'https://github.com/username/repositoryname/tree/master/Player',
+ git_url:
+ 'https://api.github.com/repos/username/repositoryname/git/trees/tia6ISq19krmOJzbtwpCTIuwRnvpSxZY2g1FMgOp',
+ download_url:
+ 'https://raw.githubusercontent.com/username/repositoryname/master/Player',
+ type: 'file',
+ _links: {
+ self:
+ 'https://api.github.com/repos/username/repositoryname/contents/Player?ref=master',
+ git:
+ 'https://api.github.com/repos/username/repositoryname/git/trees/tia6ISq19krmOJzbtwpCTIuwRnvpSxZY2g1FMgOp',
+ html:
+ 'https://github.com/username/repositoryname/tree/master/Player',
+ },
+ },
+ ];
+ context.setState({ files: fils });
+ const dirsfils = dirs.concat(fils);
+ context.setState({ readme: 'SOMETHING READ ME BLAHHHH' });
+ context.setState({ root: true });
+ context.rerender();
+
+ const tree = render(context);
+ expect(tree).toMatchSnapshot();
+
+ const len = context.find('.activecontent__githubrepofilerow').length;
+ for (let i = 0; i < len; i += 1) {
+ const ghrow = context.find('.activecontent__githubrepofilerow').at(i);
+ expect(ghrow.exists()).toEqual(true);
+ if (dirsfils[i].type === 'dir') {
+ expect(ghrow.text()).toEqual(`📁 ${dirsfils[i].name}`);
+ } else {
+ expect(ghrow.text()).toEqual(dirsfils[i].name);
+ }
+ expect(ghrow.childAt(0).attr('href')).toEqual(dirsfils[i].html_url);
+ expect(ghrow.childAt(0).attr('data-api-url')).toEqual(dirsfils[i].url);
+ expect(ghrow.childAt(0).attr('data-path')).toEqual(dirsfils[i].path);
+ }
+ expect(context.find('.activecontent__githubrepoheader').text()).toEqual(
+ context.state('path'),
+ );
+ expect(
+ context
+ .find('.activecontent__githubrepo')
+ .childAt(2)
+ .attr('dangerouslySetInnerHTML'),
+ ).toEqual({ __html: `${marked(context.state('readme'))}` });
+ });
+ });
+});
diff --git a/app/javascript/chat/__tests__/message.test.jsx b/app/javascript/chat/__tests__/message.test.jsx
new file mode 100644
index 000000000..4cf4ecf0a
--- /dev/null
+++ b/app/javascript/chat/__tests__/message.test.jsx
@@ -0,0 +1,41 @@
+import { h } from 'preact';
+import render from 'preact-render-to-json';
+import { shallow } from 'preact-render-spy';
+import Message from '../message';
+
+const msg = {
+ username: 'asdf',
+ used_id: 12345,
+ message: 'WE BUILT THIS CITY',
+ color: '#00FFFF',
+};
+
+const getMessage = message => (
+
+);
+
+describe('', () => {
+ it('should render and test snapshot', () => {
+ const tree = render(getMessage(msg));
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('should have the proper elements, attributes and values', () => {
+ const context = shallow(getMessage(msg));
+ expect(context.find('.chatmessage').exists()).toEqual(true);
+ expect(
+ context.find('.chatmessagebody__message').attr('dangerouslySetInnerHTML'),
+ ).toEqual({ __html: msg.message });
+ expect(
+ context.find('.chatmessagebody__username').attr('style').color,
+ ).toEqual(msg.color);
+ expect(context.find('.chatmessagebody__username--link').text()).toEqual(
+ msg.username,
+ );
+ });
+});
diff --git a/app/javascript/chat/__tests__/userDetails.test.jsx b/app/javascript/chat/__tests__/userDetails.test.jsx
new file mode 100644
index 000000000..1cb57ce74
--- /dev/null
+++ b/app/javascript/chat/__tests__/userDetails.test.jsx
@@ -0,0 +1,176 @@
+import { h } from 'preact';
+import render from 'preact-render-to-json';
+import { shallow } from 'preact-render-spy';
+import UserDetails from '../userDetails';
+
+const user1 = {
+ username: 'bojackhorseman',
+ name: 'Bojack Horseman',
+ summary: 'I am the Bojack Horseman from Horsing Around and Secreteriat',
+ joined_at: 'January 2, 1964',
+ twitter_username: 'bojacktwitter',
+ github_username: 'bojackgithub',
+ website_url: 'http://bojackhorseman.com',
+ location: 'Los Angeles, CA',
+ profile_image: 'https://media.giphy.com/media/3o7WTHb2WjEXbsmWDS/giphy.gif',
+};
+
+const user2 = {
+ username: 'mrpeanutbutter',
+ name: 'Mr. Peanutbutter',
+ summary: 'Woof Woof *smile*',
+ joined_at: '1960s',
+ twitter_username: 'mrpbtwitter',
+ github_username: 'mrpbgithub',
+ website_url: 'http://mrpeanutbutter.com',
+ location: 'Los Angeles, DO(G)',
+ profile_image: 'https://media.giphy.com/media/xThuW6sWCGbpZMpX7a/giphy.gif',
+};
+
+const getUserDetails = user => ;
+
+describe('', () => {
+ describe('for user1', () => {
+ it('should render and test snapshot', () => {
+ const tree = render(getUserDetails(user1));
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('should have the appropriate elements, attributes and values', () => {
+ const context = shallow(getUserDetails(user1));
+ expect(
+ context.find('.activechatchannel__activecontentuserdetails').exists(),
+ ).toEqual(true); // only class to check for
+ const parentDiv = context.find('div').at(0);
+
+ expect(parentDiv.childAt(0)).toEqual(context.find('img').at(0));
+ expect(
+ context
+ .find('img')
+ .at(0)
+ .attr('src'),
+ ).toEqual(user1.profile_image); // profile pic
+
+ expect(parentDiv.childAt(1)).toEqual(context.find('h1'));
+ expect(context.find('h1').text()).toEqual(user1.name); // user.name
+ expect(
+ context
+ .find('a')
+ .at(0)
+ .attr('href'),
+ ).toEqual(`/${user1.username}`); // user.username
+
+ // social links
+ expect(
+ context
+ .find('a')
+ .at(1)
+ .attr('href'),
+ ).toEqual(`https://twitter.com/${user1.twitter_username}`); // twitter
+ expect(
+ context
+ .find('a')
+ .at(2)
+ .attr('href'),
+ ).toEqual(`https://github.com/${user1.github_username}`); // github
+ expect(
+ context
+ .find('a')
+ .at(3)
+ .attr('href'),
+ ).toEqual(user1.website_url); // website
+
+ expect(parentDiv.childAt(3)).toEqual(context.find('div').at(2));
+ expect(
+ context
+ .find('div')
+ .at(2)
+ .text(),
+ ).toEqual(user1.summary); // user.summary
+ expect(
+ context
+ .find('div')
+ .at(6)
+ .text(),
+ ).toEqual(user1.location); // user.location
+ expect(
+ context
+ .find('div')
+ .at(8)
+ .text(),
+ ).toEqual(user1.joined_at); // user.joined_at
+ });
+ });
+
+ describe('for user2', () => {
+ it('should render and test snapshot', () => {
+ const tree = render(getUserDetails(user2));
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('should have the appropriate elements, attributes and values', () => {
+ const context = shallow(getUserDetails(user2));
+ expect(
+ context.find('.activechatchannel__activecontentuserdetails').exists(),
+ ).toEqual(true); // only class to check for
+ const parentDiv = context.find('div').at(0);
+
+ expect(parentDiv.childAt(0)).toEqual(context.find('img').at(0));
+ expect(
+ context
+ .find('img')
+ .at(0)
+ .attr('src'),
+ ).toEqual(user2.profile_image); // profile pic
+
+ expect(parentDiv.childAt(1)).toEqual(context.find('h1'));
+ expect(context.find('h1').text()).toEqual(user2.name); // user.name
+ expect(
+ context
+ .find('a')
+ .at(0)
+ .attr('href'),
+ ).toEqual(`/${user2.username}`); // user.username
+
+ // social links
+ expect(
+ context
+ .find('a')
+ .at(1)
+ .attr('href'),
+ ).toEqual(`https://twitter.com/${user2.twitter_username}`); // twitter
+ expect(
+ context
+ .find('a')
+ .at(2)
+ .attr('href'),
+ ).toEqual(`https://github.com/${user2.github_username}`); // github
+ expect(
+ context
+ .find('a')
+ .at(3)
+ .attr('href'),
+ ).toEqual(user2.website_url); // website
+
+ expect(parentDiv.childAt(3)).toEqual(context.find('div').at(2));
+ expect(
+ context
+ .find('div')
+ .at(2)
+ .text(),
+ ).toEqual(user2.summary); // user.summary
+ expect(
+ context
+ .find('div')
+ .at(6)
+ .text(),
+ ).toEqual(user2.location); // user.location
+ expect(
+ context
+ .find('div')
+ .at(8)
+ .text(),
+ ).toEqual(user2.joined_at); // user.joined_at
+ });
+ });
+});
diff --git a/app/javascript/chat/__tests__/view.test.jsx b/app/javascript/chat/__tests__/view.test.jsx
new file mode 100644
index 000000000..eda9760cc
--- /dev/null
+++ b/app/javascript/chat/__tests__/view.test.jsx
@@ -0,0 +1,139 @@
+import { h } from 'preact';
+import render from 'preact-render-to-json';
+import { shallow } from 'preact-render-spy';
+import View from '../view';
+
+let exited = false;
+let accepted = false;
+let declined = false;
+
+const onViewExitFake = () => {
+ exited = true;
+};
+
+const handleInvitationAcceptFake = () => {
+ accepted = true;
+};
+
+const handleInvitationDeclineFake = () => {
+ declined = true;
+};
+
+const sampleChannel = [
+ {
+ channel_name: 'name',
+ description: 'some description',
+ membership_id: '12345',
+ },
+];
+
+const getView = channel => (
+
+);
+
+describe('', () => {
+ it('should render and test snapshot (no channel)', () => {
+ const tree = render(getView([]));
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('should render and test snapshot (with channel)', () => {
+ const tree = shallow(getView(sampleChannel));
+ expect(tree).toMatchSnapshot();
+ });
+
+ it('should have the proper attributes and text values (no channel provided)', () => {
+ const context = shallow(getView([]));
+ expect(context.find('.chatNonChatView').exists()).toEqual(true);
+ expect(context.find('.container').exists()).toEqual(true);
+
+ expect(context.find('.chatNonChatView_exitbutton').exists()).toEqual(true);
+ expect(context.find('.chatNonChatView_exitbutton').text()).toEqual('×');
+
+ expect(context.find('h1').exists()).toEqual(true);
+ expect(context.find('h1').text()).toEqual('Channel Invitations 🤗');
+
+ expect(context.find('.chatNonChatView_contentblock').exists()).toEqual(
+ false,
+ );
+ });
+
+ it('should have the proper attributes and text values (with channel provided)', () => {
+ const context = shallow(getView(sampleChannel));
+ expect(context.find('.chatNonChatView_contentblock').exists()).toEqual(
+ true,
+ );
+
+ expect(context.find('h2').exists()).toEqual(true);
+ expect(context.find('h2').text()).toEqual('name');
+
+ expect(context.find('em').exists()).toEqual(true);
+ expect(context.find('em').text()).toEqual('some description');
+
+ expect(context.find('.cta').exists()).toEqual(true);
+ expect(
+ context
+ .find('.cta')
+ .at(0)
+ .attr('data-content'),
+ ).toEqual('12345'); // accept button
+ expect(
+ context
+ .find('.cta')
+ .at(0)
+ .text(),
+ ).toEqual('Accept'); // accept button
+ expect(
+ context
+ .find('.cta')
+ .at(1)
+ .attr('data-content'),
+ ).toEqual('12345'); // decline button
+ expect(
+ context
+ .find('.cta')
+ .at(1)
+ .text(),
+ ).toEqual('Decline'); // accept button
+ });
+
+ it('should trigger exit', () => {
+ const context = shallow(getView([]));
+ context.find('.chatNonChatView_exitbutton').simulate('click');
+ expect(exited).toEqual(true);
+ exited = false;
+ });
+
+ it('should trigger accept', () => {
+ const context = shallow(getView(sampleChannel));
+ context
+ .find('.cta')
+ .at(0)
+ .simulate('click'); // click accept button
+
+ expect(exited).toEqual(false);
+ expect(accepted).toEqual(true);
+ expect(declined).toEqual(false);
+
+ accepted = false;
+ });
+
+ it('should trigger decline', () => {
+ const context = shallow(getView(sampleChannel));
+ context
+ .find('.cta')
+ .at(1)
+ .simulate('click'); // click decline button
+
+ expect(exited).toEqual(false);
+ expect(accepted).toEqual(false);
+ expect(declined).toEqual(true);
+
+ declined = false;
+ });
+});
diff --git a/docs/testing.md b/docs/testing.md
index 080b1f89c..e447f6bef 100644
--- a/docs/testing.md
+++ b/docs/testing.md
@@ -2,12 +2,13 @@
We use the following testing tools:
-* [**RSpec**](http://rspec.info/) for testing the backend
-* [**Capybara**](https://github.com/teamcapybara/capybara) with [**selenium-webdriver**](https://github.com/SeleniumHQ/selenium/tree/master/javascript/node/selenium-webdriver) for view testing
-* [**chromedriver-helper**](https://github.com/flavorjones/chromedriver-helper) for standard JS testing
-* [**guard-rspec**](https://github.com/guard/guard-rspec) for automated testing
-* [**Jest**](https://facebook.github.io/jest) for testing in the front-end
-* [**SimpleCov**](https://github.com/colszowka/simplecov) for tracking overall test coverage
+- [**RSpec**](http://rspec.info/) for testing the backend
+- [**Capybara**](https://github.com/teamcapybara/capybara) with [**selenium-webdriver**](https://github.com/SeleniumHQ/selenium/tree/master/javascript/node/selenium-webdriver) for view testing
+- [**chromedriver-helper**](https://github.com/flavorjones/chromedriver-helper) for standard JS testing
+- [**guard-rspec**](https://github.com/guard/guard-rspec) for automated testing
+- [**Jest**](https://facebook.github.io/jest) for testing in the front-end
+- [**preact-render-spy**](https://github.com/mzgoddard/preact-render-spy) for testing Preact components.
+- [**SimpleCov**](https://github.com/colszowka/simplecov) for tracking overall test coverage
Each pull request should come with tests related to the newly written feature or bug fix. Ideally, we should test both the front end and back end.