>(middlewares);
11 |
12 | describe('todos usecases', () => {
13 | describe('addAndSync', () => {
14 | it('returns actions', () => {
15 | const store = mockStore({
16 | todos: {},
17 | });
18 |
19 | const action = Usecases.addAndSync('me', { title: 'foo' });
20 | return store.dispatch(action).then(() => {
21 | const dispatchedActions = store.getActions();
22 | expect(dispatchedActions.length).toBe(1);
23 |
24 | const [dispatchedAction] = dispatchedActions;
25 | expect(dispatchedAction.type).toBe(ADD);
26 | expect(dispatchedAction.payload.todo.title).toBe('foo');
27 | });
28 | });
29 | });
30 |
31 | describe('removeAndSync', () => {
32 | it('returns actions', () => {
33 | const now = new Date();
34 | const store = mockStore({
35 | todos: {
36 | it: {
37 | id: 'it',
38 | title: 'foo',
39 | detail: 'bar',
40 | createdAt: now.toISOString(),
41 | updatedAt: now.toISOString(),
42 | completedAt: null,
43 | },
44 | },
45 | });
46 |
47 | const action = Usecases.removeAndSync('me', 'it');
48 | return store.dispatch(action).then(() => {
49 | const dispatchedActions = store.getActions();
50 | expect(dispatchedActions.length).toBe(1);
51 |
52 | const [dispatchedAction] = dispatchedActions;
53 | expect(dispatchedAction.type).toBe(REMOVE);
54 | expect(dispatchedAction.payload.id).toBe('it');
55 | });
56 | });
57 | });
58 |
59 | describe('toggleAndSync', () => {
60 | it('returns actions', () => {
61 | const now = new Date();
62 | const store = mockStore({
63 | todos: {
64 | it: {
65 | id: 'it',
66 | title: 'foo',
67 | detail: 'bar',
68 | createdAt: now.toISOString(),
69 | updatedAt: now.toISOString(),
70 | completedAt: null,
71 | },
72 | },
73 | });
74 |
75 | const action = Usecases.toggleAndSync('me', 'it');
76 | return store.dispatch(action).then(() => {
77 | const dispatchedActions = store.getActions();
78 | expect(dispatchedActions.length).toBe(1);
79 |
80 | const [dispatchedAction] = dispatchedActions;
81 | expect(dispatchedAction.type).toBe(TOGGLE);
82 | expect(dispatchedAction.payload.id).toBe('it');
83 | expect(dispatchedAction.payload.completedAt).not.toBeNull();
84 | });
85 | });
86 | });
87 |
88 | describe('editAndSync', () => {
89 | it('returns actions', () => {
90 | const now = new Date();
91 | const store = mockStore({
92 | todos: {
93 | it: {
94 | id: 'it',
95 | title: 'foo',
96 | detail: 'bar',
97 | createdAt: now.toISOString(),
98 | updatedAt: now.toISOString(),
99 | completedAt: null,
100 | },
101 | },
102 | });
103 |
104 | const action = Usecases.editAndSync('me', 'it', { title: 'edited' });
105 | return store.dispatch(action).then(() => {
106 | const dispatchedActions = store.getActions();
107 | expect(dispatchedActions.length).toBe(1);
108 |
109 | const [dispatchedAction] = dispatchedActions;
110 | expect(dispatchedAction.type).toBe(UPDATE);
111 | expect(dispatchedAction.payload.id).toBe('it');
112 | expect(dispatchedAction.payload.todo.title).toBe('edited');
113 | });
114 | });
115 | });
116 | });
117 |
--------------------------------------------------------------------------------
/privacy-policy.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | プライバシーポリシー
9 |
10 |
11 |
12 | プライバシーポリシー
13 | legio(以下,「当サークル」といいます。)は,本ウェブサイト上で提供するサービス(以下,「本サービス」といいます。)における,ユーザーの個人情報の取扱いについて,以下のとおりプライバシーポリシー(以下,「本ポリシー」といいます。)を定めます。
14 |
15 | 第1条(個人情報)
16 | 「個人情報」とは,個人情報保護法にいう「個人情報」を指すものとし,生存する個人に関する情報であって,当該情報に含まれる氏名,生年月日,住所,電話番号,連絡先その他の記述等により特定の個人を識別できる情報及び容貌,指紋,声紋にかかるデータ,及び健康保険証の保険者番号などの当該情報単体から特定の個人を識別できる情報(個人識別情報)を指します。
17 |
18 | 第2条(個人情報の収集方法)
19 | 当サークルは,ユーザーが利用登録をする際にメールアドレスなどの個人情報をお尋ねすることがあります。また,ユーザーと提携先などとの間でなされたユーザーの個人情報を含む取引記録や決済に関する情報を,当サークルの提携先(情報提供元,広告主,広告配信先などを含みます。以下,「提携先」といいます。)などから収集することがあります。
20 |
21 | 第3条(個人情報を収集・利用する目的)
22 | 当サークルが個人情報を収集・利用する目的は,以下のとおりです。
23 |
24 | - 当サークルサービスの提供・運営のため
25 | - ユーザーからのお問い合わせに回答するため(本人確認を行うことを含む)
26 | - ユーザーが利用中のサービスの新機能,更新情報,キャンペーン等及び当サークルが提供する他のサービスの案内のメールを送付するため
27 | - メンテナンス,重要なお知らせなど必要に応じたご連絡のため
28 | - 利用規約に違反したユーザーや,不正・不当な目的でサービスを利用しようとするユーザーの特定をし,ご利用をお断りするため
29 | - ユーザーにご自身の登録情報の閲覧や変更,削除,ご利用状況の閲覧を行っていただくため
30 | - 有料サービスにおいて,ユーザーに利用料金を請求するため
31 | - 上記の利用目的に付随する目的
32 |
33 |
34 | 第4条(利用目的の変更)
35 | 当サークルは,利用目的が変更前と関連性を有すると合理的に認められる場合に限り,個人情報の利用目的を変更するものとします。
36 | 利用目的の変更を行った場合には,変更後の目的について,当サークル所定の方法により,ユーザーに通知し,または本ウェブサイト上に公表するものとします。
37 |
38 | 第5条(個人情報の第三者提供)
39 | 当サークルは,次に掲げる場合を除いて,あらかじめユーザーの同意を得ることなく,第三者に個人情報を提供することはありません。ただし,個人情報保護法その他の法令で認められる場合を除きます。
40 |
41 |
42 | - 人の生命,身体または財産の保護のために必要がある場合であって,本人の同意を得ることが困難であるとき
43 | - 公衆衛生の向上または児童の健全な育成の推進のために特に必要がある場合であって,本人の同意を得ることが困難であるとき
44 | - 国の機関もしくは地方公共団体またはその委託を受けた者が法令の定める事務を遂行することに対して協力する必要がある場合であって,本人の同意を得ることにより当該事務の遂行に支障を及ぼすおそれがあるとき
45 | - 予め次の事項を告知あるいは公表し,かつ当サークルが個人情報保護委員会に届出をしたとき
46 |
47 | - 利用目的に第三者への提供を含むこと
48 | - 第三者に提供されるデータの項目
49 | - 第三者への提供の手段または方法
50 | - 本人の求めに応じて個人情報の第三者への提供を停止すること
51 | - 本人の求めを受け付ける方法
52 |
53 |
54 | - 前項の定めにかかわらず,次に掲げる場合には,当該情報の提供先は第三者に該当しないものとします。
55 |
56 | - 当サークルが利用目的の達成に必要な範囲内において個人情報の取扱いの全部または一部を委託する場合
57 | - 合併その他の事由による事業の承継に伴って個人情報が提供される場合
58 | - 個人情報を特定の者との間で共同して利用する場合であって,その旨並びに共同して利用される個人情報の項目,共同して利用する者の範囲,利用する者の利用目的および当該個人情報の管理について責任を有する者の氏名または名称について,あらかじめ本人に通知し,または本人が容易に知り得る状態に置いた場合
59 |
60 |
61 |
62 |
63 | 第6条(個人情報の開示)
64 | 当サークルは,本人から個人情報の開示を求められたときは,本人に対し,遅滞なくこれを開示します。ただし,開示することにより次のいずれかに該当する場合は,その全部または一部を開示しないこともあり,開示しない決定をした場合には,その旨を遅滞なく通知します。なお,個人情報の開示に際しては,1件あたり1,000円の手数料を申し受けます。
65 |
66 |
67 | - 本人または第三者の生命,身体,財産その他の権利利益を害するおそれがある場合
68 | - 当サークルの業務の適正な実施に著しい支障を及ぼすおそれがある場合
69 | - その他法令に違反することとなる場合
70 |
71 |
72 | 前項の定めにかかわらず,履歴情報および特性情報などの個人情報以外の情報については,原則として開示いたしません。
73 |
74 | 第7条(個人情報の訂正および削除)
75 | ユーザーは,当サークルの保有する自己の個人情報が誤った情報である場合には,当サークルが定める手続きにより,当サークルに対して個人情報の訂正,追加または削除(以下,「訂正等」といいます。)を請求することができます。
76 | 当サークルは,ユーザーから前項の請求を受けてその請求に応じる必要があると判断した場合には,遅滞なく,当該個人情報の訂正等を行うものとします。
77 | 当サークルは,前項の規定に基づき訂正等を行った場合,または訂正等を行わない旨の決定をしたときは遅滞なく,これをユーザーに通知します。
78 |
79 | 第8条(個人情報の利用停止等)
80 | 当サークルは,本人から,個人情報が,利用目的の範囲を超えて取り扱われているという理由,または不正の手段により取得されたものであるという理由により,その利用の停止または消去(以下,「利用停止等」といいます。)を求められた場合には,遅滞なく必要な調査を行います。
81 | 前項の調査結果に基づき,その請求に応じる必要があると判断した場合には,遅滞なく,当該個人情報の利用停止等を行います。
82 | 当サークルは,前項の規定に基づき利用停止等を行った場合,または利用停止等を行わない旨の決定をしたときは,遅滞なく,これをユーザーに通知します。
83 | 前2項にかかわらず,利用停止等に多額の費用を有する場合その他利用停止等を行うことが困難な場合であって,ユーザーの権利利益を保護するために必要なこれに代わるべき措置をとれる場合は,この代替策を講じるものとします。
84 |
85 | 第9条(プライバシーポリシーの変更)
86 | 本ポリシーの内容は,法令その他本ポリシーに別段の定めのある事項を除いて,ユーザーに通知することなく,変更することができるものとします。
87 | 当サークルが別途定める場合を除いて,変更後のプライバシーポリシーは,本ウェブサイトに掲載したときから効力を生じるものとします。
88 |
89 | 第10条(お問い合わせ窓口)
90 | 本ポリシーに関するお問い合わせは,下記の窓口までお願いいたします。
91 |
92 | Eメールアドレス:janus.wel.3@gmail.com
93 |
94 |
95 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "praiser",
3 | "version": "1.1.1",
4 | "private": true,
5 | "scripts": {
6 | "android": "react-native run-android --appIdSuffix debug",
7 | "ios": "react-native run-ios",
8 | "start": "react-native start",
9 | "test": "jest --testPathIgnorePatterns e2e/*",
10 | "test-e2e-ios": "node ./scripts/test-e2e --ios",
11 | "test-e2e-android": "node ./scripts/test-e2e --android",
12 | "coverage": "jest --testPathIgnorePatterns e2e/* --coverage",
13 | "lint": "eslint index.js $(find src -name '*.ts' -a -not -name '*.d.ts' -o -name '*.tsx')",
14 | "type-check": "tsc --noEmit",
15 | "generate-typings": "node bin/generate-typings.js > src/@types/react-native-dotenv.d.ts"
16 | },
17 | "dependencies": {
18 | "@januswel/object-utilities": "1.0.1",
19 | "@react-native-community/async-storage": "https://github.com/januswel/async-storage.git#add-typings-of-mock",
20 | "@react-native-community/google-signin": "4.0.0",
21 | "@react-native-community/masked-view": "0.1.7",
22 | "@react-native-firebase/analytics": "6.3.4",
23 | "@react-native-firebase/app": "6.3.4",
24 | "@react-native-firebase/auth": "6.3.4",
25 | "@react-native-firebase/firestore": "6.3.4",
26 | "@react-navigation/bottom-tabs": "5.2.1",
27 | "@react-navigation/drawer": "5.3.1",
28 | "@react-navigation/native": "5.1.0",
29 | "@react-navigation/routers": "5.1.1",
30 | "@react-navigation/stack": "5.2.1",
31 | "@react-navigation/tabs": "0.0.0-alpha.12",
32 | "color": "3.1.2",
33 | "react": "16.9.0",
34 | "react-native": "0.61.5",
35 | "react-native-gesture-handler": "1.6.0",
36 | "react-native-get-random-values": "1.3.0",
37 | "react-native-paper": "3.6.0",
38 | "react-native-reanimated": "1.7.0",
39 | "react-native-safe-area-context": "0.7.3",
40 | "react-native-safe-area-view": "1.0.0",
41 | "react-native-screens": "2.3.0",
42 | "react-native-snap-carousel": "3.8.4",
43 | "react-native-svg": "12.0.3",
44 | "react-native-svg-charts": "5.3.0",
45 | "react-native-swipe-list-view": "2.4.0",
46 | "react-native-vector-icons": "6.6.0",
47 | "react-redux": "7.2.0",
48 | "redux": "4.0.5",
49 | "redux-thunk": "2.3.0",
50 | "reselect": "4.0.0",
51 | "uuid": "7.0.2"
52 | },
53 | "devDependencies": {
54 | "@babel/core": "7.8.7",
55 | "@babel/runtime": "7.8.7",
56 | "@react-native-community/eslint-config": "0.0.7",
57 | "@types/color": "3.0.1",
58 | "@types/jest": "25.1.4",
59 | "@types/react": "16.9.23",
60 | "@types/react-native": "0.61.23",
61 | "@types/react-native-snap-carousel": "3.7.4",
62 | "@types/react-native-svg-charts": "5.0.3",
63 | "@types/react-native-vector-icons": "6.4.5",
64 | "@types/react-navigation": "3.4.0",
65 | "@types/react-redux": "7.1.7",
66 | "@types/react-test-renderer": "16.9.2",
67 | "@types/redux-mock-store": "1.0.2",
68 | "@types/uuid": "7.0.2",
69 | "@typescript-eslint/eslint-plugin": "2.24.0",
70 | "@typescript-eslint/parser": "2.24.0",
71 | "babel-jest": "25.1.0",
72 | "detox": "16.0.1",
73 | "eslint": "6.8.0",
74 | "eslint-config-prettier": "6.10.0",
75 | "eslint-plugin-jest": "23.8.2",
76 | "eslint-plugin-prettier": "3.1.2",
77 | "eslint-plugin-react": "7.19.0",
78 | "jest": "25.1.0",
79 | "js-yaml": "3.13.1",
80 | "metro-react-native-babel-preset": "0.58.0",
81 | "prettier": "1.19.1",
82 | "react-test-renderer": "16.13.0",
83 | "redux-mock-store": "1.5.4",
84 | "typescript": "3.8.3"
85 | },
86 | "jest": {
87 | "preset": "react-native"
88 | },
89 | "detox": {
90 | "configurations": {
91 | "ios.sim.debug": {
92 | "binaryPath": "ios/build/Build/Products/Debug-iphonesimulator/praiser.app",
93 | "build": "xcodebuild -workspace ios/praiser.xcworkspace -scheme praiser -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build",
94 | "type": "ios.simulator",
95 | "device": {
96 | "type": "iPhone 11 Pro"
97 | }
98 | },
99 | "ios.sim.release": {
100 | "binaryPath": "ios/build/Build/Products/Release-iphonesimulator/praiser.app",
101 | "build": "xcodebuild -workspace ios/praiser.xcworkspace -scheme praiser -configuration Release -sdk iphonesimulator -derivedDataPath ios/build",
102 | "type": "ios.simulator",
103 | "device": {
104 | "type": "iPhone 11 Pro"
105 | }
106 | },
107 | "android.emu.debug": {
108 | "binaryPath": "android/app/build/outputs/apk/debug/app-debug.apk",
109 | "build": "cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug && cd ..",
110 | "type": "android.emulator",
111 | "device": {
112 | "avdName": "Pixel_2_API_28"
113 | }
114 | },
115 | "android.emu.release": {
116 | "binaryPath": "android/app/build/outputs/apk/release/app-release.apk",
117 | "build": "cd android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release && cd ..",
118 | "type": "android.emulator",
119 | "device": {
120 | "avdName": "Pixel_2_API_28"
121 | }
122 | }
123 | },
124 | "test-runner": "jest"
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/routes/Main/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
3 | import { createDrawerNavigator } from '@react-navigation/drawer';
4 | import {
5 | CHOOSE_LOGIN,
6 | HOME,
7 | INITIAL,
8 | LOADING,
9 | SIGN_IN,
10 | SIGN_UP,
11 | STATISTICS,
12 | USER_INFO,
13 | INPUT,
14 | } from '../../constants/path';
15 | import Home from './Home';
16 | import UserInfo from './UserInfo';
17 | import Statistics from './Statistics';
18 | import { ChooseLogin, Initial } from '../../components/pages';
19 | import { Loading, SignIn, SignUp, Input } from '../../containers';
20 | import { headerStyle, headerTintColor } from '../Header';
21 | import * as UiContext from '../../contexts/ui';
22 | import { createStackNavigator, StackCardInterpolationProps } from '@react-navigation/stack';
23 | import { COLOR } from '../../constants/theme';
24 |
25 | const drawerStyle = {
26 | backgroundColor: COLOR.MAIN,
27 | };
28 | const drawerContentOptions = {
29 | activeTintColor: COLOR.PRIMARY,
30 | inactiveTintColor: COLOR.WHITE,
31 | };
32 | const HomeDrawer = createDrawerNavigator();
33 |
34 | function HomeWithDrawer() {
35 | return (
36 |
37 |
38 |
39 |
40 | );
41 | }
42 |
43 | const StatisticsDrawer = createDrawerNavigator();
44 | function StatisticsWithDrawer() {
45 | return (
46 |
47 |
48 |
49 |
50 | );
51 | }
52 | const cardStyle = {
53 | backgroundColor: COLOR.MAIN,
54 | };
55 | const getActiveRouteName = (state: any): string => {
56 | if (!state || !state.routes) {
57 | return '';
58 | }
59 | const route = state.routes[state.index];
60 |
61 | if (route.state) {
62 | // Dive into nested navigators
63 | return getActiveRouteName(route.state);
64 | }
65 |
66 | return route.name;
67 | };
68 |
69 | const Tab = createBottomTabNavigator();
70 |
71 | function TabRoutes() {
72 | return (
73 | {
82 | const routeName = getActiveRouteName(props.route.state);
83 | return {
84 | tabBarVisible: routeName !== USER_INFO,
85 | };
86 | }}
87 | >
88 |
89 |
90 |
91 | );
92 | }
93 |
94 | const ModalStack = createStackNavigator();
95 | function TabWithModalRoutes() {
96 | return (
97 |
104 |
105 |
106 |
107 | );
108 | }
109 | const ChooseLoginStack = createStackNavigator();
110 | function ChooseLoginNavigator() {
111 | return (
112 |
120 |
121 |
122 |
123 |
124 | );
125 | }
126 |
127 | const forFade = ({ current }: StackCardInterpolationProps) => ({
128 | cardStyle: {
129 | opacity: current.progress,
130 | },
131 | });
132 | const Stack = createStackNavigator();
133 | function switchingAuthStatus(status: UiContext.Status) {
134 | switch (status) {
135 | case UiContext.Status.UN_AUTHORIZED:
136 | return ;
137 | case UiContext.Status.AUTHORIZED:
138 | return ;
139 | case UiContext.Status.FIRST_OPEN:
140 | default:
141 | return ;
142 | }
143 | }
144 | function AuthWithRoutes() {
145 | const uiContext = React.useContext(UiContext.Context);
146 | return (
147 |
148 | {uiContext.applicationState !== UiContext.Status.LOADING ? (
149 | switchingAuthStatus(uiContext.applicationState)
150 | ) : (
151 |
152 | )}
153 |
154 | );
155 | }
156 |
157 | export default AuthWithRoutes;
158 |
--------------------------------------------------------------------------------
/ios/praiser.xcodeproj/xcshareddata/xcschemes/praiser-tvOS.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
43 |
49 |
50 |
51 |
52 |
53 |
58 |
59 |
61 |
67 |
68 |
69 |
70 |
71 |
77 |
78 |
79 |
80 |
81 |
82 |
92 |
94 |
100 |
101 |
102 |
103 |
104 |
105 |
111 |
113 |
119 |
120 |
121 |
122 |
124 |
125 |
128 |
129 |
130 |
--------------------------------------------------------------------------------
/src/modules/todos.test.ts:
--------------------------------------------------------------------------------
1 | import { Todo, Todos } from '../domain/models';
2 | import {
3 | Action,
4 | ADD,
5 | REMOVE,
6 | SET,
7 | TOGGLE,
8 | UPDATE,
9 | add,
10 | createInitialState,
11 | default as reducer,
12 | remove,
13 | set,
14 | toggle,
15 | update,
16 | } from './todos';
17 |
18 | describe('todos', () => {
19 | describe('set', () => {
20 | it('returns an Action to tell the reducer "set todos"', () => {
21 | const action = set(Todos.factory([{ title: 'foo', detail: 'bar' }, { title: 'buz' }]));
22 | expect(action.type).toBe(SET);
23 | expect(Todos.getNumof(action.payload.todos)).toBe(2);
24 | });
25 | });
26 |
27 | describe('add', () => {
28 | it('returns an Action to tell the reducer "add a todo"', () => {
29 | const action = add(Todo.factory({ title: 'foo', detail: 'bar' }));
30 |
31 | expect(action.type).toBe(ADD);
32 | expect(action.payload.todo.id.length).toBe(36);
33 | expect(action.payload.todo.title).toBe('foo');
34 | expect(action.payload.todo.detail).toBe('bar');
35 | expect(action.payload.todo.createdAt).toBe(action.payload.todo.updatedAt);
36 | });
37 | });
38 |
39 | describe('update', () => {
40 | it('returns an Action to tell the reducer "update the todo"', () => {
41 | const action = update('4', { title: 'foo', detail: 'bar' });
42 | expect(action).toEqual({
43 | type: UPDATE,
44 | payload: {
45 | id: '4',
46 | todo: {
47 | title: 'foo',
48 | detail: 'bar',
49 | },
50 | },
51 | });
52 | });
53 | });
54 |
55 | describe('remove', () => {
56 | it('returns an Action to tell the reducer "remove the todo"', () => {
57 | const action = remove('0');
58 | expect(action).toEqual({
59 | type: REMOVE,
60 | payload: {
61 | id: '0',
62 | },
63 | });
64 | });
65 | });
66 |
67 | describe('toggle', () => {
68 | it('returns an Action to tell the reducer "toggle completed status of the todo"', () => {
69 | const action = toggle('42');
70 | expect(action).toEqual({
71 | type: TOGGLE,
72 | payload: {
73 | id: '42',
74 | },
75 | });
76 | });
77 | });
78 |
79 | describe('reducer', () => {
80 | describe('set Action', () => {
81 | it('returns a new state that has payload of "set Action"', () => {
82 | const action = set(Todos.factory([{ title: 'foo', detail: 'bar' }, { title: 'buz' }]));
83 | const setState = reducer(undefined, action);
84 | expect(Todos.getNumof(setState)).toBe(2);
85 | });
86 | });
87 |
88 | describe('add Action', () => {
89 | it('returns a new state that has a instance of Todo model from payload of "add Action"', () => {
90 | const action = add(Todo.factory({ title: 'foo', detail: 'bar' }));
91 | const addedState = reducer(createInitialState(), action);
92 | expect(Todos.getNumof(addedState)).toBe(1);
93 | const [id] = Object.keys(addedState);
94 | const addedTodo = addedState[id];
95 | expect(addedTodo.id.length).toBe(36);
96 | expect(addedTodo.title).toBe('foo');
97 | expect(addedTodo.detail).toBe('bar');
98 | expect(addedTodo.createdAt).toBe(addedTodo.updatedAt);
99 | });
100 | });
101 |
102 | describe('update Action', () => {
103 | it('returns a new state that has the instance of Todo model updated by "update Action"', () => {
104 | const setAction = set(Todos.factory([{ title: 'foo' }]));
105 | const initialState = reducer(undefined, setAction);
106 |
107 | const [id] = Object.keys(initialState);
108 | const updateAction = update(id, { title: 'bar', detail: 'buz' });
109 | const updatedState = reducer(initialState, updateAction);
110 | expect(Todos.getNumof(updatedState)).toBe(1);
111 |
112 | const updatedTodo = updatedState[id];
113 | expect(updatedTodo.title).toBe('bar');
114 | expect(updatedTodo.detail).toBe('buz');
115 | });
116 | });
117 |
118 | describe('remove Action', () => {
119 | it('returns a new state that does not have the instance of Todo model specified by "remove Action"', () => {
120 | const setAction = set(Todos.factory([{ title: 'foo' }, { title: 'bar' }]));
121 | const initialState = reducer(undefined, setAction);
122 |
123 | const [id] = Object.keys(initialState);
124 | const removeAction = remove(id);
125 | const removedState = reducer(initialState, removeAction);
126 | expect(Todos.getNumof(removedState)).toBe(1);
127 | expect(id in removedState).toBe(false);
128 | });
129 | });
130 |
131 | describe('toggle Action', () => {
132 | it('returns a new state that has the instance of Todo model which is toggled by "toggle Action"', () => {
133 | const setAction = set(Todos.factory([{ title: 'foo' }, { title: 'bar' }]));
134 | const initialState = reducer(undefined, setAction);
135 |
136 | const [id] = Object.keys(initialState);
137 | const toggleAction = toggle(id);
138 | const toggledState = reducer(initialState, toggleAction);
139 | expect(Todos.getNumof(toggledState)).toBe(2);
140 |
141 | const toggledTodo = toggledState[id];
142 | expect(Todo.isDone(toggledTodo)).toBe(true);
143 | });
144 | });
145 |
146 | describe('unknown Action', () => {
147 | it('returns old state', () => {
148 | const action = ({ type: 'unknown' } as unknown) as Action;
149 | const initialState = createInitialState();
150 | const state = reducer(initialState, action);
151 | expect(state).toBe(initialState);
152 | });
153 | });
154 | });
155 | });
156 |
--------------------------------------------------------------------------------
/ios/praiser.xcodeproj/xcshareddata/xcschemes/praiser.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
11 |
14 |
15 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
33 |
39 |
40 |
41 |
47 |
53 |
54 |
55 |
61 |
67 |
68 |
69 |
70 |
71 |
76 |
77 |
83 |
84 |
85 |
86 |
88 |
94 |
95 |
96 |
97 |
98 |
108 |
110 |
116 |
117 |
118 |
119 |
125 |
127 |
133 |
134 |
135 |
136 |
138 |
139 |
142 |
143 |
144 |
--------------------------------------------------------------------------------