Commit f0c912bf1f2a4157f8583e1922edfd5247346701
1 parent
d1f05315dd
Exists in
master
add method tests and airbnb linting via eslint
Showing
38 changed files
with
436 additions
and
345 deletions
Show diff stats
.meteor/packages
1 | # Meteor packages used by this project, one per line. | 1 | # Meteor packages used by this project, one per line. |
2 | # Check this file (and the other files in this directory) into your repository. | 2 | # Check this file (and the other files in this directory) into your repository. |
3 | # | 3 | # |
4 | # 'meteor add' and 'meteor remove' will edit this file for you, | 4 | # 'meteor add' and 'meteor remove' will edit this file for you, |
5 | # but you can also edit it by hand. | 5 | # but you can also edit it by hand. |
6 | 6 | ||
7 | meteor-base # Packages every Meteor app needs to have | 7 | meteor-base # Packages every Meteor app needs to have |
8 | mobile-experience # Packages for a great mobile UX | 8 | mobile-experience # Packages for a great mobile UX |
9 | mongo # The database Meteor supports right now | 9 | mongo # The database Meteor supports right now |
10 | reactive-var # Reactive variable for tracker | 10 | reactive-var # Reactive variable for tracker |
11 | session | 11 | session |
12 | jquery # Helpful client-side library | 12 | jquery # Helpful client-side library |
13 | tracker # Meteor's client-side reactive programming library | 13 | tracker # Meteor's client-side reactive programming library |
14 | 14 | ||
15 | standard-minifier-css # CSS minifier run for production mode | 15 | standard-minifier-css # CSS minifier run for production mode |
16 | standard-minifier-js # JS minifier run for production mode | 16 | standard-minifier-js # JS minifier run for production mode |
17 | es5-shim # ECMAScript 5 compatibility for older browsers. | 17 | es5-shim # ECMAScript 5 compatibility for older browsers. |
18 | ecmascript # Enable ECMAScript2015+ syntax in app code | 18 | ecmascript # Enable ECMAScript2015+ syntax in app code |
19 | 19 | ||
20 | accounts-password | 20 | accounts-password |
21 | accounts-base | 21 | accounts-base |
22 | check | 22 | check |
23 | audit-argument-checks | 23 | audit-argument-checks |
24 | browser-policy | 24 | browser-policy |
25 | 25 | ||
26 | fourseven:scss | 26 | fourseven:scss |
27 | aldeed:collection2 | 27 | aldeed:collection2 |
28 | # momentjs:moment NPM this | 28 | # momentjs:moment NPM this |
29 | alanning:roles | 29 | alanning:roles |
30 | react-meteor-data | 30 | react-meteor-data |
31 | themeteorchef:jquery-validation | 31 | themeteorchef:jquery-validation |
32 | themeteorchef:bert | 32 | themeteorchef:bert |
33 | static-html | 33 | static-html |
34 | xolvio:cleaner | 34 | xolvio:cleaner |
35 | practicalmeteor:mocha | 35 | practicalmeteor:mocha |
36 | xolvio:backdoor | 36 | xolvio:backdoor |
37 | xolvio:email-stub | 37 | xolvio:email-stub |
38 | mdg:validated-method | 38 | mdg:validated-method |
39 | dburles:factory | ||
40 | aldeed:simple-schema | ||
39 | 41 |
.meteor/versions
1 | accounts-base@1.2.5 | 1 | accounts-base@1.2.5 |
2 | accounts-password@1.1.7 | 2 | accounts-password@1.1.7 |
3 | alanning:roles@1.2.15 | 3 | alanning:roles@1.2.15 |
4 | aldeed:collection2@2.9.1 | 4 | aldeed:collection2@2.9.1 |
5 | aldeed:collection2-core@1.1.1 | 5 | aldeed:collection2-core@1.1.1 |
6 | aldeed:schema-deny@1.0.1 | 6 | aldeed:schema-deny@1.0.1 |
7 | aldeed:schema-index@1.0.1 | 7 | aldeed:schema-index@1.0.1 |
8 | aldeed:simple-schema@1.5.3 | 8 | aldeed:simple-schema@1.5.3 |
9 | allow-deny@1.0.3 | 9 | allow-deny@1.0.3 |
10 | audit-argument-checks@1.0.6 | 10 | audit-argument-checks@1.0.6 |
11 | autoupdate@1.2.7 | 11 | autoupdate@1.2.7 |
12 | babel-compiler@6.6.1 | 12 | babel-compiler@6.6.1 |
13 | babel-runtime@0.1.7 | 13 | babel-runtime@0.1.7 |
14 | base64@1.0.7 | 14 | base64@1.0.7 |
15 | binary-heap@1.0.7 | 15 | binary-heap@1.0.7 |
16 | blaze@2.1.6 | 16 | blaze@2.1.6 |
17 | blaze-tools@1.0.7 | 17 | blaze-tools@1.0.7 |
18 | boilerplate-generator@1.0.7 | 18 | boilerplate-generator@1.0.7 |
19 | browser-policy@1.0.8 | 19 | browser-policy@1.0.8 |
20 | browser-policy-common@1.0.8 | 20 | browser-policy-common@1.0.8 |
21 | browser-policy-content@1.0.9 | 21 | browser-policy-content@1.0.9 |
22 | browser-policy-framing@1.0.9 | 22 | browser-policy-framing@1.0.9 |
23 | caching-compiler@1.0.3 | 23 | caching-compiler@1.0.3 |
24 | caching-html-compiler@1.0.5 | 24 | caching-html-compiler@1.0.5 |
25 | callback-hook@1.0.7 | 25 | callback-hook@1.0.7 |
26 | check@1.1.3 | 26 | check@1.1.3 |
27 | coffeescript@1.0.16 | 27 | coffeescript@1.0.16 |
28 | dburles:factory@0.4.2 | ||
28 | ddp@1.2.4 | 29 | ddp@1.2.4 |
29 | ddp-client@1.2.4 | 30 | ddp-client@1.2.4 |
30 | ddp-common@1.2.4 | 31 | ddp-common@1.2.4 |
31 | ddp-rate-limiter@1.0.3 | 32 | ddp-rate-limiter@1.0.3 |
32 | ddp-server@1.2.5 | 33 | ddp-server@1.2.5 |
33 | deps@1.0.11 | 34 | deps@1.0.11 |
34 | diff-sequence@1.0.4 | 35 | diff-sequence@1.0.4 |
35 | ecmascript@0.4.2 | 36 | ecmascript@0.4.2 |
36 | ecmascript-runtime@0.2.9 | 37 | ecmascript-runtime@0.2.9 |
37 | ejson@1.0.10 | 38 | ejson@1.0.10 |
38 | email@1.0.11 | 39 | email@1.0.11 |
39 | es5-shim@4.5.9 | 40 | es5-shim@4.5.9 |
40 | fastclick@1.0.10 | 41 | fastclick@1.0.10 |
41 | fortawesome:fontawesome@4.4.0_1 | 42 | fortawesome:fontawesome@4.5.0 |
42 | fourseven:scss@3.4.1 | 43 | fourseven:scss@3.4.3 |
43 | geojson-utils@1.0.7 | 44 | geojson-utils@1.0.7 |
44 | hot-code-push@1.0.3 | 45 | hot-code-push@1.0.3 |
45 | html-tools@1.0.8 | 46 | html-tools@1.0.8 |
46 | htmljs@1.0.8 | 47 | htmljs@1.0.8 |
47 | http@1.1.4 | 48 | http@1.1.4 |
48 | id-map@1.0.6 | 49 | id-map@1.0.6 |
49 | jquery@1.11.7 | 50 | jquery@1.11.7 |
50 | launch-screen@1.0.10 | 51 | launch-screen@1.0.10 |
51 | livedata@1.0.17 | 52 | livedata@1.0.17 |
52 | localstorage@1.0.8 | 53 | localstorage@1.0.8 |
53 | logging@1.0.11 | 54 | logging@1.0.11 |
54 | mdg:validated-method@1.0.2 | 55 | mdg:validated-method@1.1.0 |
55 | mdg:validation-error@0.5.1 | 56 | mdg:validation-error@0.5.1 |
56 | meteor@1.1.13 | 57 | meteor@1.1.13 |
57 | meteor-base@1.0.3 | 58 | meteor-base@1.0.3 |
58 | minifier-css@1.1.10 | 59 | minifier-css@1.1.10 |
59 | minifier-js@1.1.10 | 60 | minifier-js@1.1.10 |
60 | minimongo@1.0.13 | 61 | minimongo@1.0.13 |
61 | mobile-experience@1.0.3 | 62 | mobile-experience@1.0.3 |
62 | mobile-status-bar@1.0.11 | 63 | mobile-status-bar@1.0.11 |
63 | modules@0.5.2 | 64 | modules@0.5.2 |
64 | modules-runtime@0.6.2 | 65 | modules-runtime@0.6.2 |
65 | mongo@1.1.6 | 66 | mongo@1.1.6 |
66 | mongo-id@1.0.3 | 67 | mongo-id@1.0.3 |
67 | npm-bcrypt@0.7.8_2 | 68 | npm-bcrypt@0.7.8_2 |
68 | npm-mongo@1.4.42 | 69 | npm-mongo@1.4.42 |
69 | observe-sequence@1.0.10 | 70 | observe-sequence@1.0.10 |
70 | ordered-dict@1.0.6 | 71 | ordered-dict@1.0.6 |
71 | practicalmeteor:chai@2.1.0_1 | 72 | practicalmeteor:chai@2.1.0_1 |
72 | practicalmeteor:loglevel@1.2.0_2 | 73 | practicalmeteor:loglevel@1.2.0_2 |
73 | practicalmeteor:mocha@2.1.0_8 | 74 | practicalmeteor:mocha@2.4.5_1 |
74 | practicalmeteor:mocha-core@0.1.4 | 75 | practicalmeteor:mocha-core@0.1.4 |
75 | practicalmeteor:sinon@1.14.1_2 | 76 | practicalmeteor:sinon@1.14.1_2 |
76 | promise@0.6.6 | 77 | promise@0.6.6 |
77 | raix:eventemitter@0.1.3 | 78 | raix:eventemitter@0.1.3 |
78 | random@1.0.8 | 79 | random@1.0.8 |
79 | rate-limit@1.0.3 | 80 | rate-limit@1.0.3 |
80 | react-meteor-data@0.2.7 | 81 | react-meteor-data@0.2.9 |
81 | reactive-dict@1.1.6 | 82 | reactive-dict@1.1.6 |
82 | reactive-var@1.0.8 | 83 | reactive-var@1.0.8 |
83 | reload@1.1.7 | 84 | reload@1.1.7 |
84 | retry@1.0.6 | 85 | retry@1.0.6 |
85 | routepolicy@1.0.9 | 86 | routepolicy@1.0.9 |
86 | service-configuration@1.0.8 | 87 | service-configuration@1.0.8 |
87 | session@1.1.4 | 88 | session@1.1.4 |
88 | sha@1.0.6 | 89 | sha@1.0.6 |
89 | spacebars@1.0.10 | 90 | spacebars@1.0.10 |
90 | spacebars-compiler@1.0.10 | 91 | spacebars-compiler@1.0.10 |
91 | srp@1.0.7 | 92 | srp@1.0.7 |
92 | standard-minifier-css@1.0.5 | 93 | standard-minifier-css@1.0.5 |
93 | standard-minifier-js@1.0.5 | 94 | standard-minifier-js@1.0.5 |
94 | static-html@1.0.6 | 95 | static-html@1.0.6 |
95 | templating@1.1.8 | 96 | templating@1.1.8 |
96 | templating-tools@1.0.3 | 97 | templating-tools@1.0.3 |
97 | themeteorchef:bert@2.1.0 | 98 | themeteorchef:bert@2.1.0 |
98 | themeteorchef:jquery-validation@1.14.0 | 99 | themeteorchef:jquery-validation@1.14.0 |
99 | tmeasday:check-npm-versions@0.2.0 | 100 | tmeasday:check-npm-versions@0.2.0 |
100 | tmeasday:test-reporter-helpers@0.2.1 | 101 | tmeasday:test-reporter-helpers@0.2.1 |
101 | tracker@1.0.12 | 102 | tracker@1.0.12 |
102 | ui@1.0.10 | 103 | ui@1.0.10 |
103 | underscore@1.0.7 | 104 | underscore@1.0.7 |
104 | url@1.0.8 | 105 | url@1.0.8 |
105 | webapp@1.2.7 | 106 | webapp@1.2.7 |
106 | webapp-hashing@1.0.8 | 107 | webapp-hashing@1.0.8 |
107 | xolvio:backdoor@0.1.2 | 108 | xolvio:backdoor@0.2.0 |
108 | xolvio:cleaner@0.2.0 | 109 | xolvio:cleaner@0.3.0 |
109 | xolvio:email-stub@0.2.0 | 110 | xolvio:email-stub@0.2.0 |
110 | 111 |
imports/api/documents/documents.js
1 | import { Mongo } from 'meteor/mongo'; | 1 | import { Mongo } from 'meteor/mongo'; |
2 | import faker from 'faker'; | ||
2 | 3 | ||
3 | export const Documents = new Mongo.Collection( 'Documents' ); | 4 | export const Documents = new Mongo.Collection('Documents'); |
5 | |||
6 | Documents.schema = new SimpleSchema({ | ||
7 | title: { | ||
8 | type: String, | ||
9 | label: 'The title of the document.', | ||
10 | }, | ||
11 | }); | ||
12 | |||
13 | Documents.attachSchema(Documents.schema); | ||
14 | |||
15 | Factory.define('document', Documents, { | ||
16 | title: () => faker.hacker.phrase(), | ||
17 | }); | ||
4 | 18 |
imports/api/documents/documents.tests.js
File was created | 1 | /* eslint-env mocha */ | |
2 | /* eslint-disable func-names, prefer-arrow-callback */ | ||
3 | |||
4 | import { chai } from 'meteor/practicalmeteor:chai'; | ||
5 | import { Documents } from './documents.js'; | ||
6 | |||
7 | const { assert } = chai; | ||
8 | |||
9 | describe('Documents collection', function () { | ||
10 | it('registers the collection with Mongo properly', function () { | ||
11 | assert.equal(typeof Documents, 'object'); | ||
12 | }); | ||
13 | }); | ||
14 |
imports/api/documents/methods.js
1 | import { Documents } from './documents'; | 1 | import { Documents } from './documents'; |
2 | 2 | ||
3 | export const insertDocument = new ValidatedMethod({ | 3 | export const insertDocument = new ValidatedMethod({ |
4 | name: 'documents.insert', | 4 | name: 'documents.insert', |
5 | validate: new SimpleSchema({ | 5 | validate: new SimpleSchema({ |
6 | title: { type: String } | 6 | title: { type: String }, |
7 | }).validator(), | 7 | }).validator(), |
8 | run( document ) { | 8 | run(document) { |
9 | Documents.insert( document ); | 9 | Documents.insert(document); |
10 | } | 10 | }, |
11 | }); | 11 | }); |
12 | 12 | ||
13 | export const updateDocument = new ValidatedMethod({ | 13 | export const updateDocument = new ValidatedMethod({ |
14 | name: 'documents.update', | 14 | name: 'documents.update', |
15 | validate: new SimpleSchema({ | 15 | validate: new SimpleSchema({ |
16 | _id: { type: String }, | 16 | _id: { type: String }, |
17 | 'update.title': { type: String, optional: true } | 17 | 'update.title': { type: String, optional: true }, |
18 | }).validator(), | 18 | }).validator(), |
19 | run( { _id, update } ) { | 19 | run({ _id, update }) { |
20 | Documents.update( _id, { $set: update } ); | 20 | Documents.update(_id, { $set: update }); |
21 | } | 21 | }, |
22 | }); | 22 | }); |
23 | 23 | ||
24 | export const removeDocument = new ValidatedMethod({ | 24 | export const removeDocument = new ValidatedMethod({ |
25 | name: 'documents.remove', | 25 | name: 'documents.remove', |
26 | validate: new SimpleSchema({ | 26 | validate: new SimpleSchema({ |
27 | _id: { type: String } | 27 | _id: { type: String }, |
28 | }).validator(), | 28 | }).validator(), |
29 | run( { _id } ) { | 29 | run({ _id }) { |
30 | Documents.remove( _id ); | 30 | Documents.remove(_id); |
31 | } | 31 | }, |
32 | }); | 32 | }); |
33 | 33 |
imports/api/documents/methods.tests.js
File was created | 1 | /* eslint-env mocha */ | |
2 | /* eslint-disable func-names, prefer-arrow-callback */ | ||
3 | |||
4 | import { assert } from 'meteor/practicalmeteor:chai'; | ||
5 | import { resetDatabase } from 'meteor/xolvio:cleaner'; | ||
6 | import { Documents } from './documents.js'; | ||
7 | import { insertDocument, updateDocument, removeDocument } from './methods.js'; | ||
8 | |||
9 | describe('Documents methods', function () { | ||
10 | beforeEach(function () { | ||
11 | if (Meteor.isServer) { | ||
12 | resetDatabase(); | ||
13 | } | ||
14 | }); | ||
15 | |||
16 | it('inserts a document into the Documents collection', function () { | ||
17 | insertDocument.call({ title: 'You can\'t arrest me, I\'m the Cake Boss!' }); | ||
18 | const getDocument = Documents.findOne({ title: 'You can\'t arrest me, I\'m the Cake Boss!' }); | ||
19 | assert.equal(getDocument.title, 'You can\'t arrest me, I\'m the Cake Boss!'); | ||
20 | }); | ||
21 | |||
22 | it('updates a document in the Documents collection', function () { | ||
23 | const { _id } = Factory.create('document'); | ||
24 | |||
25 | updateDocument.call({ | ||
26 | _id, | ||
27 | update: { | ||
28 | title: 'You can\'t arrest me, I\'m the Cake Boss!', | ||
29 | }, | ||
30 | }); | ||
31 | |||
32 | const getDocument = Documents.findOne(_id); | ||
33 | assert.equal(getDocument.title, 'You can\'t arrest me, I\'m the Cake Boss!'); | ||
34 | }); | ||
35 | |||
36 | it('removes a document from the Documents collection', function () { | ||
37 | const { _id } = Factory.create('document'); | ||
38 | removeDocument.call({ _id }); | ||
39 | const getDocument = Documents.findOne(_id); | ||
40 | assert.equal(getDocument, undefined); | ||
41 | }); | ||
42 | }); | ||
43 |
imports/api/documents/server/publications.js
1 | import { Documents } from '../documents'; | 1 | import { Documents } from '../documents'; |
2 | 2 | ||
3 | Meteor.publish( 'documents', function() { | 3 | Meteor.publish('documents', () => Documents.find()); |
4 | return Documents.find(); | ||
5 | }); | ||
6 | 4 |
imports/modules/get-input-value.js
1 | export const getInputValue = ( component, ref, nested ) => { | 1 | export const getInputValue = (component, ref, nested) => { |
2 | let element = component.refs[ ref ]; | 2 | const element = component.refs[ref]; |
3 | return nested ? element.refs.input.value : element.value; | 3 | return nested ? element.refs.input.value : element.value; |
4 | } | 4 | }; |
5 | 5 |
imports/modules/login.js
1 | import { browserHistory } from 'react-router'; | 1 | import { browserHistory } from 'react-router'; |
2 | 2 | ||
3 | let component; | 3 | let component; |
4 | 4 | ||
5 | const _handleLogin = () => { | 5 | const _handleLogin = () => { |
6 | // <Input /> component value is accessed via nested refs. | 6 | // <Input /> component value is accessed via nested refs. |
7 | const email = component.refs.emailAddress.refs.input.value, | 7 | const email = component.refs.emailAddress.refs.input.value; |
8 | password = component.refs.password.value; | 8 | const password = component.refs.password.value; |
9 | 9 | ||
10 | Meteor.loginWithPassword( email, password, ( error ) => { | 10 | Meteor.loginWithPassword(email, password, (error) => { |
11 | if ( error ) { | 11 | if (error) { |
12 | Bert.alert( error.reason, 'warning' ); | 12 | Bert.alert(error.reason, 'warning'); |
13 | } else { | 13 | } else { |
14 | browserHistory.push( '/' ); | 14 | browserHistory.push('/'); |
15 | Bert.alert( 'Logged in!', 'success' ); | 15 | Bert.alert('Logged in!', 'success'); |
16 | } | 16 | } |
17 | }); | 17 | }); |
18 | }; | 18 | }; |
19 | 19 | ||
20 | const _validate = () => { | 20 | const _validate = () => { |
21 | $( component.refs.login ).validate({ | 21 | $(component.refs.login).validate({ |
22 | rules: { | 22 | rules: { |
23 | emailAddress: { | 23 | emailAddress: { |
24 | required: true, | 24 | required: true, |
25 | email: true | 25 | email: true, |
26 | }, | 26 | }, |
27 | password: { | 27 | password: { |
28 | required: true | 28 | required: true, |
29 | } | 29 | }, |
30 | }, | 30 | }, |
31 | messages: { | 31 | messages: { |
32 | emailAddress: { | 32 | emailAddress: { |
33 | required: 'Need an email address here.', | 33 | required: 'Need an email address here.', |
34 | email: 'Is this email address legit?' | 34 | email: 'Is this email address legit?', |
35 | }, | 35 | }, |
36 | password: { | 36 | password: { |
37 | required: 'Need a password here.' | 37 | required: 'Need a password here.', |
38 | } | 38 | }, |
39 | }, | 39 | }, |
40 | submitHandler() { _handleLogin(); } | 40 | submitHandler() { _handleLogin(); }, |
41 | }); | 41 | }); |
42 | }; | 42 | }; |
43 | 43 | ||
44 | export const handleLogin = ( options ) => { | 44 | export const handleLogin = (options) => { |
45 | component = options.component; | 45 | component = options.component; |
46 | _validate(); | 46 | _validate(); |
47 | }; | 47 | }; |
48 | 48 |
imports/modules/recover-password.js
1 | import { getInputValue } from './get-input-value'; | 1 | import { getInputValue } from './get-input-value'; |
2 | 2 | ||
3 | let component; | 3 | let component; |
4 | 4 | ||
5 | const _handleRecovery = () => { | 5 | const _handleRecovery = () => { |
6 | Accounts.forgotPassword({ | 6 | Accounts.forgotPassword({ |
7 | email: getInputValue( component, 'emailAddress', true ) | 7 | email: getInputValue(component, 'emailAddress', true), |
8 | }, ( error ) => { | 8 | }, (error) => { |
9 | if ( error ) { | 9 | if (error) { |
10 | Bert.alert( error.reason, 'warning' ); | 10 | Bert.alert(error.reason, 'warning'); |
11 | } else { | 11 | } else { |
12 | Bert.alert( 'Check your inbox for a reset link!', 'success' ); | 12 | Bert.alert('Check your inbox for a reset link!', 'success'); |
13 | } | 13 | } |
14 | }); | 14 | }); |
15 | }; | 15 | }; |
16 | 16 | ||
17 | const _validate = () => { | 17 | const _validate = () => { |
18 | $( component.refs.recoverPassword ).validate({ | 18 | $(component.refs.recoverPassword).validate({ |
19 | rules: { | 19 | rules: { |
20 | emailAddress: { | 20 | emailAddress: { |
21 | required: true, | 21 | required: true, |
22 | email: true | 22 | email: true, |
23 | } | 23 | }, |
24 | }, | 24 | }, |
25 | messages: { | 25 | messages: { |
26 | emailAddress: { | 26 | emailAddress: { |
27 | required: 'Need an email address here.', | 27 | required: 'Need an email address here.', |
28 | email: 'Is this email address legit?' | 28 | email: 'Is this email address legit?', |
29 | } | 29 | }, |
30 | }, | 30 | }, |
31 | submitHandler() { _handleRecovery(); } | 31 | submitHandler() { _handleRecovery(); }, |
32 | }); | 32 | }); |
33 | }; | 33 | }; |
34 | 34 | ||
35 | export const handleRecoverPassword = ( options ) => { | 35 | export const handleRecoverPassword = (options) => { |
36 | component = options.component; | 36 | component = options.component; |
37 | _validate(); | 37 | _validate(); |
38 | }; | 38 | }; |
39 | 39 |
imports/modules/reset-password.js
1 | import { getInputValue } from './get-input-value'; | 1 | import { getInputValue } from './get-input-value'; |
2 | import { browserHistory } from 'react-router'; | 2 | import { browserHistory } from 'react-router'; |
3 | 3 | ||
4 | let component, | 4 | let component; |
5 | token; | 5 | let token; |
6 | 6 | ||
7 | const _handleReset = () => { | 7 | const _handleReset = () => { |
8 | const password = getInputValue( component, 'newPassword', true ); | 8 | const password = getInputValue(component, 'newPassword', true); |
9 | Accounts.resetPassword( token, password, ( error ) => { | 9 | Accounts.resetPassword(token, password, (error) => { |
10 | if ( error ) { | 10 | if (error) { |
11 | Bert.alert( error.reason, 'danger' ); | 11 | Bert.alert(error.reason, 'danger'); |
12 | } else { | 12 | } else { |
13 | browserHistory.push( '/' ); | 13 | browserHistory.push('/'); |
14 | Bert.alert( 'Password reset!', 'success' ); | 14 | Bert.alert('Password reset!', 'success'); |
15 | } | 15 | } |
16 | }); | 16 | }); |
17 | }; | 17 | }; |
18 | 18 | ||
19 | const _validate = () => { | 19 | const _validate = () => { |
20 | $( component.refs.resetPassword ).validate({ | 20 | $(component.refs.resetPassword).validate({ |
21 | rules: { | 21 | rules: { |
22 | newPassword: { | 22 | newPassword: { |
23 | required: true, | 23 | required: true, |
24 | minlength: 6 | 24 | minlength: 6, |
25 | }, | 25 | }, |
26 | repeatNewPassword: { | 26 | repeatNewPassword: { |
27 | required: true, | 27 | required: true, |
28 | minlength: 6, | 28 | minlength: 6, |
29 | equalTo: '[name="newPassword"]' | 29 | equalTo: '[name="newPassword"]', |
30 | } | 30 | }, |
31 | }, | 31 | }, |
32 | messages: { | 32 | messages: { |
33 | newPassword: { | 33 | newPassword: { |
34 | required: "Enter a new password, please.", | 34 | required: 'Enter a new password, please.', |
35 | minlength: "Use at least six characters, please." | 35 | minlength: 'Use at least six characters, please.', |
36 | }, | 36 | }, |
37 | repeatNewPassword: { | 37 | repeatNewPassword: { |
38 | required: "Repeat your new password, please.", | 38 | required: 'Repeat your new password, please.', |
39 | equalTo: "Hmm, your passwords don't match. Try again?" | 39 | equalTo: 'Hmm, your passwords don\'t match. Try again?', |
40 | } | 40 | }, |
41 | }, | 41 | }, |
42 | submitHandler() { _handleReset(); } | 42 | submitHandler() { _handleReset(); }, |
43 | }); | 43 | }); |
44 | }; | 44 | }; |
45 | 45 | ||
46 | export const handleResetPassword = ( options ) => { | 46 | export const handleResetPassword = (options) => { |
47 | component = options.component; | 47 | component = options.component; |
48 | token = options.token; | 48 | token = options.token; |
49 | _validate(); | 49 | _validate(); |
50 | }; | 50 | }; |
51 | 51 |
imports/modules/signup.js
1 | import { browserHistory } from 'react-router'; | 1 | import { browserHistory } from 'react-router'; |
2 | import { getInputValue } from './get-input-value'; | 2 | import { getInputValue } from './get-input-value'; |
3 | 3 | ||
4 | let component; | 4 | let component; |
5 | 5 | ||
6 | const _getUserData = () => { | 6 | const _getUserData = () => ({ |
7 | return { | 7 | email: getInputValue(component, 'emailAddress', true), |
8 | email: getInputValue( component, 'emailAddress', true ), | 8 | password: getInputValue(component, 'password', true), |
9 | password: getInputValue( component, 'password', true ), | 9 | profile: { |
10 | profile: { | 10 | name: { |
11 | name: { | 11 | first: getInputValue(component, 'firstName', true), |
12 | first: getInputValue( component, 'firstName', true ), | 12 | last: getInputValue(component, 'lastName', true), |
13 | last: getInputValue( component, 'lastName', true ) | 13 | }, |
14 | } | 14 | }, |
15 | } | 15 | }); |
16 | }; | ||
17 | }; | ||
18 | 16 | ||
19 | const _handleSignup = () => { | 17 | const _handleSignup = () => { |
20 | const user = _getUserData(); | 18 | const user = _getUserData(); |
21 | 19 | ||
22 | Accounts.createUser( user, ( error ) => { | 20 | Accounts.createUser(user, (error) => { |
23 | if ( error ) { | 21 | if (error) { |
24 | Bert.alert( error.reason, 'danger' ); | 22 | Bert.alert(error.reason, 'danger'); |
25 | } else { | 23 | } else { |
26 | browserHistory.push( '/' ); | 24 | browserHistory.push('/'); |
27 | Bert.alert( 'Welcome!', 'success' ); | 25 | Bert.alert('Welcome!', 'success'); |
28 | } | 26 | } |
29 | }); | 27 | }); |
30 | }; | 28 | }; |
31 | 29 | ||
32 | const _validate = () => { | 30 | const _validate = () => { |
33 | $( component.refs.signup ).validate({ | 31 | $(component.refs.signup).validate({ |
34 | rules: { | 32 | rules: { |
35 | firstName: { | 33 | firstName: { |
36 | required: true | 34 | required: true, |
37 | }, | 35 | }, |
38 | lastName: { | 36 | lastName: { |
39 | required: true | 37 | required: true, |
40 | }, | 38 | }, |
41 | emailAddress: { | 39 | emailAddress: { |
42 | required: true, | 40 | required: true, |
43 | email: true | 41 | email: true, |
44 | }, | 42 | }, |
45 | password: { | 43 | password: { |
46 | required: true, | 44 | required: true, |
47 | minlength: 6 | 45 | minlength: 6, |
48 | } | 46 | }, |
49 | }, | 47 | }, |
50 | messages: { | 48 | messages: { |
51 | firstName: { | 49 | firstName: { |
52 | required: 'First name?' | 50 | required: 'First name?', |
53 | }, | 51 | }, |
54 | lastName: { | 52 | lastName: { |
55 | required: 'Last name?' | 53 | required: 'Last name?', |
56 | }, | 54 | }, |
57 | emailAddress: { | 55 | emailAddress: { |
58 | required: 'Need an email address here.', | 56 | required: 'Need an email address here.', |
59 | email: 'Is this email address legit?' | 57 | email: 'Is this email address legit?', |
60 | }, | 58 | }, |
61 | password: { | 59 | password: { |
62 | required: 'Need a password here.', | 60 | required: 'Need a password here.', |
63 | minlength: 'Use at least six characters, please.' | 61 | minlength: 'Use at least six characters, please.', |
64 | } | 62 | }, |
65 | }, | 63 | }, |
66 | submitHandler() { _handleSignup(); } | 64 | submitHandler() { _handleSignup(); }, |
67 | }); | 65 | }); |
68 | }; | 66 | }; |
69 | 67 | ||
70 | export const handleSignup = ( options ) => { | 68 | export const handleSignup = (options) => { |
71 | component = options.component; | 69 | component = options.component; |
72 | _validate(); | 70 | _validate(); |
73 | }; | 71 | }; |
74 | 72 |
imports/startup/client/index.js
1 | import './routes.jsx'; | 1 | import './routes.js'; |
2 | 2 | ||
3 | Bert.defaults.style = 'growl-top-right'; | 3 | Bert.defaults.style = 'growl-top-right'; |
4 | 4 |
imports/startup/client/routes.js
File was created | 1 | import React from 'react'; | |
2 | import { render } from 'react-dom'; | ||
3 | import { Router, Route, Redirect, IndexRoute, browserHistory } from 'react-router'; | ||
4 | |||
5 | import { App } from '../../ui/layouts/app'; | ||
6 | import { Documents } from '../../ui/pages/documents'; | ||
7 | import { Index } from '../../ui/pages/index'; | ||
8 | import { Login } from '../../ui/pages/login'; | ||
9 | import { NotFound } from '../../ui/pages/not-found'; | ||
10 | import { RecoverPassword } from '../../ui/pages/recover-password'; | ||
11 | import { ResetPassword } from '../../ui/pages/reset-password'; | ||
12 | import { Signup } from '../../ui/pages/signup'; | ||
13 | |||
14 | const requireAuth = (nextState, replace) => { | ||
15 | if (!Meteor.loggingIn() && !Meteor.user()) { | ||
16 | replace({ | ||
17 | pathname: '/login', | ||
18 | state: { nextPathName: nextState.location.pathname }, | ||
19 | }); | ||
20 | } | ||
21 | }; | ||
22 | |||
23 | Meteor.startup(() => { | ||
24 | render( | ||
25 | <Router history={ browserHistory }> | ||
26 | <Route path="/" component={ App }> | ||
27 | <IndexRoute name="index" component={ Index } onEnter={ requireAuth } /> | ||
28 | <Route name="documents" path="/documents" component={ Documents } onEnter={ requireAuth } /> | ||
29 | <Route name="login" path="/login" component={ Login } /> | ||
30 | <Route name="recover-password" path="/recover-password" component={ RecoverPassword } /> | ||
31 | <Route name="reset-password" path="/reset-password/:token" component={ ResetPassword } /> | ||
32 | <Route name="signup" path="/signup" component={ Signup } /> | ||
33 | <Route path="*" component={ NotFound } /> | ||
34 | </Route> | ||
35 | </Router>, | ||
36 | document.getElementById('react-root') | ||
37 | ); | ||
38 | }); | ||
39 |
imports/startup/client/routes.jsx
1 | import React from 'react'; | File was deleted | |
2 | import { render } from 'react-dom'; | ||
3 | import { Router, Route, Redirect, IndexRoute, browserHistory } from 'react-router'; | ||
4 | |||
5 | import { App } from '../../ui/layouts/app'; | ||
6 | import { Documents } from '../../ui/pages/documents'; | ||
7 | import { Index } from '../../ui/pages/index'; | ||
8 | import { Login } from '../../ui/pages/login'; | ||
9 | import { NotFound } from '../../ui/pages/not-found'; | ||
10 | import { RecoverPassword } from '../../ui/pages/recover-password'; | ||
11 | import { ResetPassword } from '../../ui/pages/reset-password'; | ||
12 | import { Signup } from '../../ui/pages/signup'; | ||
13 | |||
14 | const requireAuth = ( nextState, replace ) => { | ||
15 | if ( !Meteor.loggingIn() && !Meteor.user() ) { | ||
16 | replace({ | ||
17 | pathname: '/login', | ||
18 | state: { nextPathName: nextState.location.pathname } | ||
19 | }); | ||
20 | } | ||
21 | }; | ||
22 | |||
23 | Meteor.startup( () => { | ||
24 | render( | ||
25 | <Router history={ browserHistory }> | ||
26 | <Route path="/" component={ App }> | ||
27 | <IndexRoute name="index" component={ Index } onEnter={ requireAuth } /> | ||
28 | <Route name="documents" path="/documents" component={ Documents } onEnter={ requireAuth } /> | ||
29 | <Route name="login" path="/login" component={ Login } /> | ||
30 | <Route name="recover-password" path="/recover-password" component={ RecoverPassword } /> | ||
31 | <Route name="reset-password" path="/reset-password/:token" component={ ResetPassword } /> | ||
32 | <Route name="signup" path="/signup" component={ Signup } /> | ||
33 | <Route path="*" component={ NotFound } /> | ||
34 | </Route> | ||
35 | </Router>, | ||
36 | document.getElementById( 'react-root' ) | ||
37 | ); | ||
38 | }); | ||
39 | 1 | import React from 'react'; |
imports/startup/server/accounts/email-templates.js
1 | const name = 'Application Name', | 1 | const name = 'Application Name'; |
2 | email = '<support@application.com>', | 2 | const email = '<support@application.com>'; |
3 | from = `${ name } ${ email }`, | 3 | const from = `${name} ${email}`; |
4 | emailTemplates = Accounts.emailTemplates; | 4 | const emailTemplates = Accounts.emailTemplates; |
5 | 5 | ||
6 | emailTemplates.siteName = name; | 6 | emailTemplates.siteName = name; |
7 | emailTemplates.from = from; | 7 | emailTemplates.from = from; |
8 | 8 | ||
9 | emailTemplates.resetPassword = { | 9 | emailTemplates.resetPassword = { |
10 | subject() { | 10 | subject() { |
11 | return `[${ name }] Reset Your Password`; | 11 | return `[${name}] Reset Your Password`; |
12 | }, | 12 | }, |
13 | text( user, url ) { | 13 | text(user, url) { |
14 | let userEmail = user.emails[ 0 ].address, | 14 | const userEmail = user.emails[0].address; |
15 | urlWithoutHash = url.replace( '#/', '' ); | 15 | const urlWithoutHash = url.replace('#/', ''); |
16 | 16 | ||
17 | return `A password reset has been requested for the account related to this address (${ userEmail }). To reset the password, visit the following link:\n\n${ urlWithoutHash }\n\n If you did not request this reset, please ignore this email. If you feel something is wrong, please contact our support team: ${ email }.`; | 17 | return `A password reset has been requested for the account related to this |
18 | } | 18 | address (${userEmail}). To reset the password, visit the following link: |
19 | \n\n${urlWithoutHash}\n\n If you did not request this reset, please ignore | ||
20 | this email. If you feel something is wrong, please contact our support team: | ||
21 | ${email}.`; | ||
22 | }, | ||
19 | }; | 23 | }; |
20 | 24 |
imports/startup/server/fixtures.js
1 | const users = [{ | 1 | const users = [{ |
2 | email: 'admin@admin.com', | 2 | email: 'admin@admin.com', |
3 | password: 'password', | 3 | password: 'password', |
4 | profile: { | 4 | profile: { |
5 | name: { first: 'Carl', last: 'Winslow' } | 5 | name: { first: 'Carl', last: 'Winslow' }, |
6 | } | 6 | }, |
7 | }]; | 7 | }]; |
8 | 8 | ||
9 | users.forEach( ( { email, password, profile } ) => { | 9 | users.forEach(({ email, password, profile }) => { |
10 | const userExists = Meteor.users.findOne( { 'emails.address': email } ); | 10 | const userExists = Meteor.users.findOne({ 'emails.address': email }); |
11 | 11 | ||
12 | if ( !userExists ) { | 12 | if (!userExists) { |
13 | Accounts.createUser({ | 13 | Accounts.createUser({ email, password, profile }); |
14 | email: email, | ||
15 | password: password, | ||
16 | profile: profile | ||
17 | }); | ||
18 | } | 14 | } |
19 | }); | 15 | }); |
20 | 16 |
imports/ui/components/add-document.js
1 | import React from 'react'; | 1 | import React from 'react'; |
2 | import { Row, Col, ListGroup, ListGroupItem, Input, Alert } from 'react-bootstrap'; | 2 | import { Row, Col, ListGroup, ListGroupItem, Input, Alert } from 'react-bootstrap'; |
3 | import { insertDocument } from '../../api/documents/methods.js'; | 3 | import { insertDocument } from '../../api/documents/methods.js'; |
4 | 4 | ||
5 | const handleInsertDocument = ( event ) => { | 5 | const handleInsertDocument = (event) => { |
6 | const target = event.target, | 6 | const target = event.target; |
7 | title = target.value.trim(); | 7 | const title = target.value.trim(); |
8 | 8 | ||
9 | if ( title !== '' && event.keyCode === 13 ) { | 9 | if (title !== '' && event.keyCode === 13) { |
10 | insertDocument.call({ | 10 | insertDocument.call({ |
11 | title: title | 11 | title, |
12 | }, ( error, response ) => { | 12 | }, (error) => { |
13 | if ( error ) { | 13 | if (error) { |
14 | Bert.alert( error.reason, 'danger' ); | 14 | Bert.alert(error.reason, 'danger'); |
15 | } else { | 15 | } else { |
16 | target.value = ''; | 16 | target.value = ''; |
17 | Bert.alert( 'Document added!', 'success' ); | 17 | Bert.alert('Document added!', 'success'); |
18 | } | 18 | } |
19 | }); | 19 | }); |
20 | } | 20 | } |
21 | }; | 21 | }; |
22 | 22 | ||
23 | export const AddDocument = () => ( | 23 | export const AddDocument = () => ( |
24 | <Input | 24 | <Input |
25 | type="text" | 25 | type="text" |
26 | onKeyUp={ handleInsertDocument } | 26 | onKeyUp={ handleInsertDocument } |
27 | placeholder="Type a document title and press enter..." | 27 | placeholder="Type a document title and press enter..." |
28 | /> | 28 | /> |
29 | ) | 29 | ); |
30 | 30 |
imports/ui/components/app-navigation.js
1 | import React from 'react'; | 1 | import React from 'react'; |
2 | import { Navbar, Nav, NavItem, NavDropdown, MenuItem } from 'react-bootstrap'; | 2 | import { Navbar, Nav, NavItem, NavDropdown, MenuItem } from 'react-bootstrap'; |
3 | import { Link } from 'react-router'; | 3 | import { Link } from 'react-router'; |
4 | import { PublicNavigation } from './public-navigation'; | 4 | import { PublicNavigation } from './public-navigation'; |
5 | import { AuthenticatedNavigation } from './authenticated-navigation'; | 5 | import { AuthenticatedNavigation } from './authenticated-navigation'; |
6 | 6 | ||
7 | export class AppNavigation extends React.Component { | 7 | export class AppNavigation extends React.Component { |
8 | renderNavigation( hasUser ) { | 8 | renderNavigation(hasUser) { |
9 | return hasUser ? <AuthenticatedNavigation /> : <PublicNavigation />; | 9 | return hasUser ? <AuthenticatedNavigation /> : <PublicNavigation />; |
10 | } | 10 | } |
11 | 11 | ||
12 | render() { | 12 | render() { |
13 | return <Navbar> | 13 | return <Navbar> |
14 | <Navbar.Header> | 14 | <Navbar.Header> |
15 | <Navbar.Brand> | 15 | <Navbar.Brand> |
16 | <Link to="/">Application Name</Link> | 16 | <Link to="/">Application Name</Link> |
17 | </Navbar.Brand> | 17 | </Navbar.Brand> |
18 | <Navbar.Toggle /> | 18 | <Navbar.Toggle /> |
19 | </Navbar.Header> | 19 | </Navbar.Header> |
20 | <Navbar.Collapse> | 20 | <Navbar.Collapse> |
21 | { this.renderNavigation( this.props.hasUser ) } | 21 | { this.renderNavigation(this.props.hasUser) } |
22 | </Navbar.Collapse> | 22 | </Navbar.Collapse> |
23 | </Navbar>; | 23 | </Navbar>; |
24 | } | 24 | } |
25 | } | 25 | } |
26 | 26 |
imports/ui/components/authenticated-navigation.js
1 | import React from 'react'; | 1 | import React from 'react'; |
2 | import { browserHistory } from 'react-router'; | 2 | import { browserHistory } from 'react-router'; |
3 | import { IndexLinkContainer, LinkContainer } from 'react-router-bootstrap'; | 3 | import { IndexLinkContainer, LinkContainer } from 'react-router-bootstrap'; |
4 | import { Nav, NavItem, NavDropdown, MenuItem } from 'react-bootstrap'; | 4 | import { Nav, NavItem, NavDropdown, MenuItem } from 'react-bootstrap'; |
5 | 5 | ||
6 | const handleLogout = () => { | 6 | const handleLogout = () => Meteor.logout(() => browserHistory.push('/login')); |
7 | return Meteor.logout( () => browserHistory.push( '/login' ) ); | ||
8 | }; | ||
9 | 7 | ||
10 | const userName = () => { | 8 | const userName = () => { |
11 | const user = Meteor.user(); | 9 | const user = Meteor.user(); |
12 | if ( user ) { | 10 | const name = user && user.profile ? user.profile.name : ''; |
13 | const name = user && user.profile ? user.profile.name : ''; | 11 | return user ? `${name.first} ${name.last}` : ''; |
14 | return `${ name.first } ${ name.last }`; | ||
15 | } | ||
16 | }; | 12 | }; |
17 | 13 | ||
18 | export const AuthenticatedNavigation = () => ( | 14 | export const AuthenticatedNavigation = () => ( |
19 | <div> | 15 | <div> |
20 | <Nav> | 16 | <Nav> |
21 | <IndexLinkContainer to="/"> | 17 | <IndexLinkContainer to="/"> |
22 | <NavItem eventKey={ 1 } href="/">Index</NavItem> | 18 | <NavItem eventKey={ 1 } href="/">Index</NavItem> |
23 | </IndexLinkContainer> | 19 | </IndexLinkContainer> |
24 | <LinkContainer to="/documents"> | 20 | <LinkContainer to="/documents"> |
25 | <NavItem eventKey={ 2 } href="/documents">Documents</NavItem> | 21 | <NavItem eventKey={ 2 } href="/documents">Documents</NavItem> |
26 | </LinkContainer> | 22 | </LinkContainer> |
27 | </Nav> | 23 | </Nav> |
28 | <Nav pullRight> | 24 | <Nav pullRight> |
29 | <NavDropdown eventKey={ 3 } title={ userName() } id="basic-nav-dropdown"> | 25 | <NavDropdown eventKey={ 3 } title={ userName() } id="basic-nav-dropdown"> |
30 | <MenuItem eventKey={ 3.1 } onClick={ handleLogout }>Logout</MenuItem> | 26 | <MenuItem eventKey={ 3.1 } onClick={ handleLogout }>Logout</MenuItem> |
31 | </NavDropdown> | 27 | </NavDropdown> |
32 | </Nav> | 28 | </Nav> |
33 | </div> | 29 | </div> |
34 | ) | 30 | ); |
35 | 31 |
imports/ui/components/document.js
File was created | 1 | import React from 'react'; | |
2 | import { Row, Col, ListGroupItem, Input, Button } from 'react-bootstrap'; | ||
3 | import { updateDocument, removeDocument } from '../../api/documents/methods.js'; | ||
4 | |||
5 | const handleUpdateDocument = (documentId, event) => { | ||
6 | const title = event.target.value.trim(); | ||
7 | if (title !== '' && event.keyCode === 13) { | ||
8 | updateDocument.call({ | ||
9 | _id: documentId, | ||
10 | update: { title }, | ||
11 | }, (error) => { | ||
12 | if (error) { | ||
13 | Bert.alert(error.reason, 'danger'); | ||
14 | } else { | ||
15 | Bert.alert('Document updated!', 'success'); | ||
16 | } | ||
17 | }); | ||
18 | } | ||
19 | }; | ||
20 | |||
21 | const handleRemoveDocument = (documentId, event) => { | ||
22 | event.preventDefault(); | ||
23 | if (confirm('Are you sure? This is permanent.')) { | ||
24 | removeDocument.call({ | ||
25 | _id: documentId, | ||
26 | }, (error) => { | ||
27 | if (error) { | ||
28 | Bert.alert(error.reason, 'danger'); | ||
29 | } else { | ||
30 | Bert.alert('Document removed!', 'success'); | ||
31 | } | ||
32 | }); | ||
33 | } | ||
34 | }; | ||
35 | |||
36 | export const Document = ({ document }) => ( | ||
37 | <ListGroupItem key={ document._id }> | ||
38 | <Row> | ||
39 | <Col xs={ 8 } sm={ 10 }> | ||
40 | <Input | ||
41 | type="text" | ||
42 | standalone | ||
43 | defaultValue={ document.title } | ||
44 | onKeyUp={ handleUpdateDocument.bind(this, document._id) } | ||
45 | /> | ||
46 | </Col> | ||
47 | <Col xs={ 4 } sm={ 2 }> | ||
48 | <Button | ||
49 | bsStyle="danger" | ||
50 | className="btn-block" | ||
51 | onClick={ handleRemoveDocument.bind(this, document._id) }> | ||
52 | Remove | ||
53 | </Button> | ||
54 | </Col> | ||
55 | </Row> | ||
56 | </ListGroupItem> | ||
57 | ); | ||
58 |
imports/ui/components/documents-list.js
1 | import React from 'react'; | 1 | import React from 'react'; |
2 | import { Row, Col, ListGroup, ListGroupItem, Input, Button, Alert } from 'react-bootstrap'; | 2 | import { Row, Col, ListGroup, Alert } from 'react-bootstrap'; |
3 | import { updateDocument, removeDocument } from '../../api/documents/methods.js'; | 3 | import { Document } from './document.js'; |
4 | 4 | ||
5 | const handleUpdateDocument = ( documentId, event ) => { | 5 | export const DocumentsList = ({ documents }) => ( |
6 | const title = event.target.value.trim(); | 6 | documents.length > 0 ? <ListGroup className="documents-list"> |
7 | if ( title !== '' && event.keyCode === 13 ) { | 7 | {documents.map((doc) => ( |
8 | updateDocument.call({ | 8 | <Document key={ doc._id } document={ doc } /> |
9 | _id: documentId, | 9 | ))} |
10 | update: { title: title } | 10 | </ListGroup> : |
11 | }, ( error, response ) => { | 11 | <Alert bsStyle="warning">No documents yet.</Alert> |
12 | if ( error ) { | 12 | ); |
13 | Bert.alert( error.reason, 'danger' ); | ||
14 | } | ||
15 | }); | ||
16 | } | ||
17 | }; | ||
18 | |||
19 | const handleRemoveDocument = ( documentId, event ) => { | ||
20 | event.preventDefault(); | ||
21 | if ( confirm( 'Are you sure? This is permanent.' ) ) { | ||
22 | removeDocument.call({ | ||
23 | _id: documentId | ||
24 | }, ( error, response ) => { | ||
25 | if ( error ) { | ||
26 | Bert.alert( error.reason, 'danger' ); | ||
27 | } else { | ||
28 | Bert.alert( 'Document removed!', 'success' ); | ||
29 | } | ||
30 | }); | ||
31 | } | ||
32 | }; | ||
33 | |||
34 | export const DocumentsList = ( { documents } ) => { | ||
35 | if ( documents.length > 0 ) { | ||
36 | return <ListGroup className="documents-list"> | ||
37 | {documents.map( ( { _id, title } ) => { | ||
38 | return <ListGroupItem key={ _id }> | ||
39 | <Row> | ||
40 | <Col xs={ 8 } sm={ 10 }> | ||
41 | <Input | ||
42 | type="text" | ||
43 | standalone | ||
44 | defaultValue={ title } | ||
45 | onKeyUp={ handleUpdateDocument.bind( this, _id ) } | ||
46 | /> | ||
47 | </Col> | ||
48 | <Col xs={ 4 } sm={ 2 }> | ||
49 | <Button | ||
50 | bsStyle="danger" | ||
51 | className="btn-block" | ||
52 | onClick={ handleRemoveDocument.bind( this, _id ) }> | ||
53 | Remove | ||
54 | </Button> | ||
55 | </Col> | ||
56 | </Row> | ||
57 | </ListGroupItem>; | ||
58 | })} | ||
59 | </ListGroup>; | ||
60 | } else { | ||
61 | return <Alert bsStyle="warning">No documents yet.</Alert>; | ||
62 | } | ||
63 | }; | ||
64 | 13 |
imports/ui/components/public-navigation.js
1 | import React from 'react'; | 1 | import React from 'react'; |
2 | import { LinkContainer } from 'react-router-bootstrap'; | 2 | import { LinkContainer } from 'react-router-bootstrap'; |
3 | import { Nav, NavItem } from 'react-bootstrap'; | 3 | import { Nav, NavItem } from 'react-bootstrap'; |
4 | 4 | ||
5 | export const PublicNavigation = () => ( | 5 | export const PublicNavigation = () => ( |
6 | <Nav pullRight> | 6 | <Nav pullRight> |
7 | <LinkContainer to="signup"> | 7 | <LinkContainer to="signup"> |
8 | <NavItem eventKey={ 1 } href="/signup">Sign Up</NavItem> | 8 | <NavItem eventKey={ 1 } href="/signup">Sign Up</NavItem> |
9 | </LinkContainer> | 9 | </LinkContainer> |
10 | <LinkContainer to="login"> | 10 | <LinkContainer to="login"> |
11 | <NavItem eventKey={ 2 } href="/login">Log In</NavItem> | 11 | <NavItem eventKey={ 2 } href="/login">Log In</NavItem> |
12 | </LinkContainer> | 12 | </LinkContainer> |
13 | </Nav> | 13 | </Nav> |
14 | ) | 14 | ); |
15 | 15 |
imports/ui/containers/app-navigation.js
1 | import { composeWithTracker } from 'react-komposer'; | 1 | import { composeWithTracker } from 'react-komposer'; |
2 | import { AppNavigation } from '../components/app-navigation'; | 2 | import { AppNavigation } from '../components/app-navigation'; |
3 | 3 | ||
4 | const composer = ( props, onData ) => { | 4 | const composer = (props, onData) => { |
5 | onData( null, { hasUser: Meteor.user() } ); | 5 | onData(null, { hasUser: Meteor.user() }); |
6 | }; | 6 | }; |
7 | 7 | ||
8 | export default composeWithTracker( composer, {}, {}, { pure: false } )( AppNavigation ); | 8 | export default composeWithTracker(composer, {}, {}, { pure: false })(AppNavigation); |
9 | 9 |
imports/ui/containers/documents-list.js
1 | import React from 'react'; | 1 | import React from 'react'; |
2 | import { composeWithTracker } from 'react-komposer'; | 2 | import { composeWithTracker } from 'react-komposer'; |
3 | import { Documents } from '../../api/documents/documents.js'; | 3 | import { Documents } from '../../api/documents/documents.js'; |
4 | import { DocumentsList } from '../components/documents-list.js'; | 4 | import { DocumentsList } from '../components/documents-list.js'; |
5 | 5 | ||
6 | const composer = ( params, onReady ) => { | 6 | const composer = (params, onReady) => { |
7 | const subscription = Meteor.subscribe( 'documents' ); | 7 | const subscription = Meteor.subscribe('documents'); |
8 | if ( subscription.ready() ) { | 8 | if (subscription.ready()) { |
9 | let documents = Documents.find().fetch(); | 9 | const documents = Documents.find().fetch(); |
10 | onReady( null, { documents } ); | 10 | onReady(null, { documents }); |
11 | } | 11 | } |
12 | }; | 12 | }; |
13 | 13 | ||
14 | export default composeWithTracker( composer )( DocumentsList ); | 14 | export default composeWithTracker(composer)(DocumentsList); |
15 | 15 |
imports/ui/layouts/app.js
1 | import React from 'react'; | 1 | import React from 'react'; |
2 | import { Grid } from 'react-bootstrap'; | 2 | import { Grid } from 'react-bootstrap'; |
3 | import AppNavigation from '../containers/app-navigation'; | 3 | import AppNavigation from '../containers/app-navigation'; |
4 | 4 | ||
5 | export const App = React.createClass({ | 5 | export const App = React.createClass({ |
6 | contextTypes: { | 6 | contextTypes: { |
7 | router() { | 7 | router() { |
8 | return React.PropTypes.func.isRequired; | 8 | return React.PropTypes.func.isRequired; |
9 | } | 9 | }, |
10 | }, | 10 | }, |
11 | render() { | 11 | render() { |
12 | const { isActive } = this.context.router; | 12 | const { isActive } = this.context.router; |
13 | return <div> | 13 | return <div> |
14 | <AppNavigation activeRoute={ isActive } /> | 14 | <AppNavigation activeRoute={ isActive } /> |
15 | <Grid> | 15 | <Grid> |
16 | { this.props.children } | 16 | { this.props.children } |
17 | </Grid> | 17 | </Grid> |
18 | </div>; | 18 | </div>; |
19 | } | 19 | }, |
20 | }); | 20 | }); |
21 | 21 |
imports/ui/pages/documents.js
1 | import React from 'react'; | 1 | import React from 'react'; |
2 | import { Row, Col, Input, Button } from 'react-bootstrap'; | 2 | import { Row, Col, Input, Button } from 'react-bootstrap'; |
3 | import DocumentsList from '../containers/documents-list.js'; | 3 | import DocumentsList from '../containers/documents-list.js'; |
4 | import { AddDocument } from '../components/add-document.js'; | 4 | import { AddDocument } from '../components/add-document.js'; |
5 | 5 | ||
6 | export const Documents = () => ( | 6 | export const Documents = () => ( |
7 | <Row> | 7 | <Row> |
8 | <Col xs={ 12 }> | 8 | <Col xs={ 12 }> |
9 | <h4 className="page-header">Documents</h4> | 9 | <h4 className="page-header">Documents</h4> |
10 | <AddDocument /> | 10 | <AddDocument /> |
11 | <DocumentsList /> | 11 | <DocumentsList /> |
12 | </Col> | 12 | </Col> |
13 | </Row> | 13 | </Row> |
14 | ) | 14 | ); |
15 | 15 |
imports/ui/pages/index.js
1 | import React from 'react'; | 1 | import React from 'react'; |
2 | import { Jumbotron, Button } from 'react-bootstrap'; | 2 | import { Jumbotron, Button } from 'react-bootstrap'; |
3 | 3 | ||
4 | export const Index = () => ( | 4 | export const Index = () => ( |
5 | <Jumbotron className="text-center"> | 5 | <Jumbotron className="text-center"> |
6 | <h2>Base</h2> | 6 | <h2>Base</h2> |
7 | <p>A starting point for Meteor applications.</p> | 7 | <p>A starting point for Meteor applications.</p> |
8 | <p><a className="btn btn-success" href="https://themeteorchef.com/base" role="button">Read the Documentation</a></p> | 8 | <p><a className="btn btn-success" href="https://themeteorchef.com/base" role="button">Read the Documentation</a></p> |
9 | <p style={ { fontSize: '16px', color: '#aaa' } }>Currently at v4.0.0</p> | 9 | <p style={ { fontSize: '16px', color: '#aaa' } }>Currently at v4.0.0</p> |
10 | </Jumbotron> | 10 | </Jumbotron> |
11 | ) | 11 | ); |
12 | 12 |
imports/ui/pages/login.js
1 | import React from 'react'; | 1 | import React from 'react'; |
2 | import { Link } from 'react-router'; | 2 | import { Link } from 'react-router'; |
3 | import { Row, Col, PageHeader, Input, Button } from 'react-bootstrap'; | 3 | import { Row, Col, PageHeader, Input, Button } from 'react-bootstrap'; |
4 | import { handleLogin } from '../../modules/login'; | 4 | import { handleLogin } from '../../modules/login'; |
5 | 5 | ||
6 | export class Login extends React.Component { | 6 | export class Login extends React.Component { |
7 | componentDidMount() { | 7 | componentDidMount() { |
8 | handleLogin( { component: this } ); | 8 | handleLogin({ component: this }); |
9 | } | 9 | } |
10 | 10 | ||
11 | handleSubmit( event ) { | 11 | handleSubmit(event) { |
12 | event.preventDefault(); | 12 | event.preventDefault(); |
13 | } | 13 | } |
14 | 14 | ||
15 | render() { | 15 | render() { |
16 | return <Row> | 16 | return <Row> |
17 | <Col xs={ 12 } sm={ 6 } md={ 4 }> | 17 | <Col xs={ 12 } sm={ 6 } md={ 4 }> |
18 | <h4 className="page-header">Login</h4> | 18 | <h4 className="page-header">Login</h4> |
19 | <form ref="login" className="login" onSubmit={ this.handleSubmit }> | 19 | <form ref="login" className="login" onSubmit={ this.handleSubmit }> |
20 | <Input | 20 | <Input |
21 | type="email" | 21 | type="email" |
22 | label="Email Address" | 22 | label="Email Address" |
23 | ref="emailAddress" | 23 | ref="emailAddress" |
24 | name="emailAddress" | 24 | name="emailAddress" |
25 | placeholder="Email Address" | 25 | placeholder="Email Address" |
26 | /> | 26 | /> |
27 | <div className="form-group"> | 27 | <div className="form-group"> |
28 | <label htmlFor="password"> | 28 | <label htmlFor="password"> |
29 | <span className="pull-left">Password</span> | 29 | <span className="pull-left">Password</span> |
30 | <Link className="pull-right" to="/recover-password">Forgot Password?</Link> | 30 | <Link className="pull-right" to="/recover-password">Forgot Password?</Link> |
31 | </label> | 31 | </label> |
32 | <input type="password" className="form-control" ref="password" name="password" placeholder="Password" /> | 32 | <input |
33 | type="password" | ||
34 | className="form-control" | ||
35 | ref="password" | ||
36 | name="password" | ||
37 | placeholder="Password" | ||
38 | /> | ||
33 | </div> | 39 | </div> |
34 | <Button type="submit" bsStyle="success">Login</Button> | 40 | <Button type="submit" bsStyle="success">Login</Button> |
35 | </form> | 41 | </form> |
36 | </Col> | 42 | </Col> |
37 | </Row>; | 43 | </Row>; |
38 | } | 44 | } |
39 | } | 45 | } |
40 | 46 |
imports/ui/pages/not-found.js
1 | import React from 'react'; | 1 | import React from 'react'; |
2 | import { browserHistory } from 'react-router'; | 2 | import { browserHistory } from 'react-router'; |
3 | import { Alert } from 'react-bootstrap'; | 3 | import { Alert } from 'react-bootstrap'; |
4 | 4 | ||
5 | export const NotFound = () => ( | 5 | export const NotFound = () => ( |
6 | <Alert bsStyle="danger"> | 6 | <Alert bsStyle="danger"> |
7 | <p><strong>Error [404]</strong>: { window.location.pathname } does not exist.</p> | 7 | <p><strong>Error [404]</strong>: { window.location.pathname } does not exist.</p> |
8 | </Alert> | 8 | </Alert> |
9 | ) | 9 | ); |
10 | 10 |
imports/ui/pages/recover-password.js
1 | import React from 'react'; | 1 | import React from 'react'; |
2 | import { Row, Col, Alert, Input, Button } from 'react-bootstrap'; | 2 | import { Row, Col, Alert, Input, Button } from 'react-bootstrap'; |
3 | import { handleRecoverPassword } from '../../modules/recover-password'; | 3 | import { handleRecoverPassword } from '../../modules/recover-password'; |
4 | 4 | ||
5 | export class RecoverPassword extends React.Component { | 5 | export class RecoverPassword extends React.Component { |
6 | componentDidMount() { | 6 | componentDidMount() { |
7 | handleRecoverPassword( { component: this } ); | 7 | handleRecoverPassword({ component: this }); |
8 | } | 8 | } |
9 | 9 | ||
10 | handleSubmit( event ) { | 10 | handleSubmit(event) { |
11 | event.preventDefault(); | 11 | event.preventDefault(); |
12 | } | 12 | } |
13 | 13 | ||
14 | render() { | 14 | render() { |
15 | return <Row> | 15 | return <Row> |
16 | <Col xs={ 12 } sm={ 6 } md={ 4 }> | 16 | <Col xs={ 12 } sm={ 6 } md={ 4 }> |
17 | <h4 className="page-header">Recover Password</h4> | 17 | <h4 className="page-header">Recover Password</h4> |
18 | <Alert bsStyle="info">Enter your email address below to receive a link to reset your password.</Alert> | 18 | <Alert bsStyle="info"> |
19 | Enter your email address below to receive a link to reset your password. | ||
20 | </Alert> | ||
19 | <form ref="recoverPassword" className="recover-password" onSubmit={ this.handleSubmit }> | 21 | <form ref="recoverPassword" className="recover-password" onSubmit={ this.handleSubmit }> |
20 | <Input | 22 | <Input |
21 | type="email" | 23 | type="email" |
22 | ref="emailAddress" | 24 | ref="emailAddress" |
23 | name="emailAddress" | 25 | name="emailAddress" |
24 | placeholder="Email Address" | 26 | placeholder="Email Address" |
25 | /> | 27 | /> |
26 | <Button type="submit" bsStyle="success">Recover Password</Button> | 28 | <Button type="submit" bsStyle="success">Recover Password</Button> |
27 | </form> | 29 | </form> |
28 | </Col> | 30 | </Col> |
29 | </Row>; | 31 | </Row>; |
30 | } | 32 | } |
31 | } | 33 | } |
32 | 34 |
imports/ui/pages/reset-password.js
1 | import React from 'react'; | 1 | import React from 'react'; |
2 | import { Row, Col, Alert, Input, Button } from 'react-bootstrap'; | 2 | import { Row, Col, Alert, Input, Button } from 'react-bootstrap'; |
3 | import { handleResetPassword } from '../../modules/reset-password'; | 3 | import { handleResetPassword } from '../../modules/reset-password'; |
4 | 4 | ||
5 | export class ResetPassword extends React.Component { | 5 | export class ResetPassword extends React.Component { |
6 | componentDidMount() { | 6 | componentDidMount() { |
7 | handleResetPassword({ | 7 | handleResetPassword({ |
8 | component: this, | 8 | component: this, |
9 | token: this.props.params.token | 9 | token: this.props.params.token, |
10 | }); | 10 | }); |
11 | } | 11 | } |
12 | 12 | ||
13 | handleSubmit( event ) { | 13 | handleSubmit(event) { |
14 | event.preventDefault(); | 14 | event.preventDefault(); |
15 | } | 15 | } |
16 | 16 | ||
17 | render() { | 17 | render() { |
18 | return <Row> | 18 | return <Row> |
19 | <Col xs={ 12 } sm={ 6 } md={ 4 }> | 19 | <Col xs={ 12 } sm={ 6 } md={ 4 }> |
20 | <h4 className="page-header">Reset Password</h4> | 20 | <h4 className="page-header">Reset Password</h4> |
21 | <Alert bsStyle="info">To reset your password, enter a new one below. You will be logged in with your new password.</Alert> | 21 | <Alert bsStyle="info"> |
22 | To reset your password, enter a new one below. You will be logged in | ||
23 | with your new password. | ||
24 | </Alert> | ||
22 | <form ref="resetPassword" className="reset-password" onSubmit={ this.handleSubmit }> | 25 | <form ref="resetPassword" className="reset-password" onSubmit={ this.handleSubmit }> |
23 | <Input | 26 | <Input |
24 | label="New Password" | 27 | label="New Password" |
25 | type="password" | 28 | type="password" |
26 | ref="newPassword" | 29 | ref="newPassword" |
27 | name="newPassword" | 30 | name="newPassword" |
28 | placeholder="New Password" | 31 | placeholder="New Password" |
29 | /> | 32 | /> |
30 | <Input | 33 | <Input |
31 | label="Repeat New Password" | 34 | label="Repeat New Password" |
32 | type="password" | 35 | type="password" |
33 | ref="repeatNewPassword" | 36 | ref="repeatNewPassword" |
34 | name="repeatNewPassword" | 37 | name="repeatNewPassword" |
35 | placeholder="Repeat New Password" | 38 | placeholder="Repeat New Password" |
36 | /> | 39 | /> |
37 | <Button type="submit" bsStyle="success">Reset Password & Login</Button> | 40 | <Button type="submit" bsStyle="success">Reset Password & Login</Button> |
38 | </form> | 41 | </form> |
39 | </Col> | 42 | </Col> |
40 | </Row>; | 43 | </Row>; |
41 | } | 44 | } |
42 | } | 45 | } |
43 | 46 |
imports/ui/pages/signup.js
1 | import React from 'react'; | 1 | import React from 'react'; |
2 | import { Link } from 'react-router'; | 2 | import { Link } from 'react-router'; |
3 | import { Row, Col, PageHeader, Input, Button } from 'react-bootstrap'; | 3 | import { Row, Col, PageHeader, Input, Button } from 'react-bootstrap'; |
4 | import { handleSignup } from '../../modules/signup'; | 4 | import { handleSignup } from '../../modules/signup'; |
5 | 5 | ||
6 | export class Signup extends React.Component { | 6 | export class Signup extends React.Component { |
7 | componentDidMount() { | 7 | componentDidMount() { |
8 | handleSignup( { component: this } ); | 8 | handleSignup({ component: this }); |
9 | } | 9 | } |
10 | 10 | ||
11 | handleSubmit( event ) { | 11 | handleSubmit(event) { |
12 | event.preventDefault(); | 12 | event.preventDefault(); |
13 | } | 13 | } |
14 | 14 | ||
15 | render() { | 15 | render() { |
16 | return <Row> | 16 | return <Row> |
17 | <Col xs={ 12 } sm={ 6 } md={ 4 }> | 17 | <Col xs={ 12 } sm={ 6 } md={ 4 }> |
18 | <h4 className="page-header">Sign Up</h4> | 18 | <h4 className="page-header">Sign Up</h4> |
19 | <form ref="signup" className="signup" onSubmit={ this.handleSubmit }> | 19 | <form ref="signup" className="signup" onSubmit={ this.handleSubmit }> |
20 | <Row> | 20 | <Row> |
21 | <Col xs={ 6 } sm={ 6 }> | 21 | <Col xs={ 6 } sm={ 6 }> |
22 | <Input | 22 | <Input |
23 | type="text" | 23 | type="text" |
24 | label="First Name" | 24 | label="First Name" |
25 | ref="firstName" | 25 | ref="firstName" |
26 | name="firstName" | 26 | name="firstName" |
27 | placeholder="First Name" | 27 | placeholder="First Name" |
28 | /> | 28 | /> |
29 | </Col> | 29 | </Col> |
30 | <Col xs={ 6 } sm={ 6 }> | 30 | <Col xs={ 6 } sm={ 6 }> |
31 | <Input | 31 | <Input |
32 | type="text" | 32 | type="text" |
33 | label="Last Name" | 33 | label="Last Name" |
34 | ref="lastName" | 34 | ref="lastName" |
35 | name="lastName" | 35 | name="lastName" |
36 | placeholder="Last Name" | 36 | placeholder="Last Name" |
37 | /> | 37 | /> |
38 | </Col> | 38 | </Col> |
39 | </Row> | 39 | </Row> |
40 | <Input | 40 | <Input |
41 | type="email" | 41 | type="email" |
42 | label="Email Address" | 42 | label="Email Address" |
43 | ref="emailAddress" | 43 | ref="emailAddress" |
44 | name="emailAddress" | 44 | name="emailAddress" |
45 | placeholder="Email Address" | 45 | placeholder="Email Address" |
46 | /> | 46 | /> |
47 | <Input | 47 | <Input |
48 | type="password" | 48 | type="password" |
49 | label="Password" | 49 | label="Password" |
50 | ref="password" | 50 | ref="password" |
51 | name="password" | 51 | name="password" |
52 | placeholder="Password" | 52 | placeholder="Password" |
53 | /> | 53 | /> |
54 | <Button type="submit" bsStyle="success">Sign Up</Button> | 54 | <Button type="submit" bsStyle="success">Sign Up</Button> |
55 | </form> | 55 | </form> |
56 | <p>Already have an account? <Link to="/login">Log In</Link>.</p> | 56 | <p>Already have an account? <Link to="/login">Log In</Link>.</p> |
57 | </Col> | 57 | </Col> |
58 | </Row>; | 58 | </Row>; |
59 | } | 59 | } |
60 | } | 60 | } |
61 | 61 |
package.json
1 | { | 1 | { |
2 | "name": "application-name", | 2 | "name": "application-name", |
3 | "version": "1.0.0", | 3 | "version": "1.0.0", |
4 | "description": "Application description.", | 4 | "description": "Application description.", |
5 | "scripts": { | 5 | "scripts": { |
6 | "start": "meteor --settings settings-development.json", | 6 | "start": "meteor --settings settings-development.json", |
7 | "chimp-watch": "chimp --ddp=http://localhost:3000 --watch --mocha --path=tests", | 7 | "chimp-watch": "chimp --ddp=http://localhost:3000 --watch --mocha --path=tests", |
8 | "chimp-test": "chimp --ddp=http://localhost:3000 --mocha --path=tests", | 8 | "chimp-test": "chimp --ddp=http://localhost:3000 --mocha --path=tests", |
9 | "staging": "meteor deploy staging.meteor.com --settings settings-development.json", | 9 | "staging": "meteor deploy staging.meteor.com --settings settings-development.json", |
10 | "production": "meteor deploy production.meteor.com --settings settings-production.json" | 10 | "production": "meteor deploy production.meteor.com --settings settings-production.json" |
11 | }, | 11 | }, |
12 | "devDependencies": {}, | 12 | "devDependencies": {}, |
13 | "dependencies": { | 13 | "eslintConfig": { |
14 | "parserOptions": { | ||
15 | "ecmaFeatures": { | ||
16 | "jsx": true | ||
17 | } | ||
18 | }, | ||
19 | "plugins": [ | ||
20 | "meteor", | ||
21 | "react" | ||
22 | ], | ||
23 | "extends": [ | ||
24 | "airbnb/base", | ||
25 | "plugin:meteor/guide" | ||
26 | ], | ||
27 | "globals": { | ||
28 | "$": false, | ||
29 | "Accounts": false, | ||
30 | "Bert": false, | ||
31 | "browser": false, | ||
32 | "expect": false, | ||
33 | "Factory": false, | ||
34 | "Meteor": false, | ||
35 | "server": false, | ||
36 | "SimpleSchema": false, | ||
37 | "ValidatedMethod": false | ||
38 | }, | ||
39 | "rules": {} | ||
40 | }, | ||
41 | "devDependencies": { | ||
14 | "chimp": "^0.33.0", | 42 | "chimp": "^0.33.0", |
15 | "react": "^0.14.8", | 43 | "eslint": "^2.6.0", |
16 | "react-addons-pure-render-mixin": "^0.14.8", | 44 | "eslint-config-airbnb": "^6.2.0", |
45 | "eslint-plugin-meteor": "^3.4.0", | ||
46 | "eslint-plugin-react": "^4.2.3", | ||
47 | "faker": "^3.1.0" | ||
48 | }, | ||
49 | "dependencies": { | ||
50 | "react": "^15.0.1", | ||
51 | "react-addons-pure-render-mixin": "^15.0.1", | ||
17 | "react-bootstrap": "^0.28.4", | 52 | "react-bootstrap": "^0.28.4", |
18 | "react-dom": "^0.14.7", | 53 | "react-dom": "^15.0.1", |
19 | "react-komposer": "^1.7.1", | 54 | "react-komposer": "^1.7.1", |
20 | "react-router": "^2.0.1", | 55 | "react-router": "^2.0.1", |
21 | "react-router-bootstrap": "^0.20.1" | 56 | "react-router-bootstrap": "^0.20.1" |
22 | } | 57 | } |
23 | } | 58 | } |
24 | 59 |
tests/login.js
1 | describe( 'Log In', function() { | 1 | /* eslint-env mocha */ |
2 | beforeEach( function() { | 2 | /* eslint-disable func-names, prefer-arrow-callback */ |
3 | server.execute( function() { | 3 | |
4 | var user = Meteor.users.findOne( { 'emails.address': 'carl.winslow@abc.com' } ); | 4 | describe('Log In', function () { |
5 | if ( user ) { | 5 | beforeEach(function () { |
6 | Meteor.users.remove( user._id ); | 6 | server.execute(function () { |
7 | const user = Meteor.users.findOne({ 'emails.address': 'carl.winslow@abc.com' }); | ||
8 | if (user) { | ||
9 | Meteor.users.remove(user._id); | ||
7 | } | 10 | } |
8 | }); | 11 | }); |
9 | }); | 12 | }); |
10 | 13 | ||
11 | it( 'should allow us to login', function() { | 14 | it('should allow us to login', function () { |
12 | server.execute( function() { | 15 | server.execute(function () { |
13 | Accounts.createUser({ | 16 | Accounts.createUser({ |
14 | email: 'carl.winslow@abc.com', | 17 | email: 'carl.winslow@abc.com', |
15 | password: 'bigguy1989', | 18 | password: 'bigguy1989', |
16 | profile: { | 19 | profile: { |
17 | name: { first: 'Carl', last: 'Winslow' } | 20 | name: { first: 'Carl', last: 'Winslow' }, |
18 | } | 21 | }, |
19 | }); | 22 | }); |
20 | }); | 23 | }); |
21 | 24 | ||
22 | browser.url( 'http://localhost:3000/login' ) | 25 | browser.url('http://localhost:3000/login') |
23 | .setValue( '[name="emailAddress"]', 'carl.winslow@abc.com' ) | 26 | .setValue('[name="emailAddress"]', 'carl.winslow@abc.com') |
24 | .setValue( '[name="password"]', 'bigguy1989' ) | 27 | .setValue('[name="password"]', 'bigguy1989') |
25 | .submitForm( 'form' ); | 28 | .submitForm('form'); |
26 | 29 | ||
27 | browser.waitForExist( '.jumbotron' ); | 30 | browser.waitForExist('.jumbotron'); |
28 | expect( browser.getUrl() ).to.equal( 'http://localhost:3000/' ); | 31 | expect(browser.getUrl()).to.equal('http://localhost:3000/'); |
29 | }); | 32 | }); |
30 | }); | 33 | }); |
31 | 34 |
tests/not-found.js
1 | describe( '404 Error', function() { | 1 | /* eslint-env mocha */ |
2 | it( 'should render a 404 for a non-existent route', function() { | 2 | /* eslint-disable func-names, prefer-arrow-callback */ |
3 | browser.url( 'http://localhost:3000/dididothat' ) | ||
4 | .waitForExist( '.alert-danger' ); | ||
5 | 3 | ||
6 | expect( browser.getText( '.alert-danger p' ) ).to.equal( 'Error [404]: /dididothat does not exist.' ); | 4 | describe('404 Error', function () { |
5 | it('should render a 404 for a non-existent route', function () { | ||
6 | browser.url('http://localhost:3000/dididothat') | ||
7 | .waitForExist('.alert-danger'); | ||
8 | |||
9 | expect(browser.getText('.alert-danger p')).to.equal('Error [404]: /dididothat does not exist.'); | ||
7 | }); | 10 | }); |
tests/reset-password.js
1 | describe( 'Reset Password', function() { | File was deleted | |
2 | beforeEach( function() { | ||
3 | server.execute( function() { | ||
4 | var user = Meteor.users.findOne( { 'emails.address': 'carl.winslow@abc.com' } ); | ||
5 | if ( user ) { Meteor.users.remove( user._id ); } | ||
6 | |||
7 | Accounts.createUser({ | ||
8 | email: 'carl.winslow@abc.com', | ||
9 | password: 'bigguy1989', | ||
10 | profile: { | ||
11 | name: { first: 'Carl', last: 'Winslow' } | ||
12 | } | ||
13 | }); | ||
14 | }); | ||
15 | }); | ||
16 | |||
17 | it( 'should send a recovery email when we request a reset @watch', function() { | ||
18 | browser.url( 'http://localhost:3000/recover-password' ) | ||
19 | .setValue( '[name="emailAddress"]', 'carl.winslow@abc.com' ) | ||
20 | .submitForm( 'form' ); | ||
21 | |||
22 | let emails = server.call( 'emailStub/getEmails' ); | ||
23 | console.log( emails[0] ); // Why no value? | ||
24 | |||
25 | // browser.url( 'http://localhost:3000' ); | ||
26 | // browser.execute( function() { | ||
27 | // Accounts.onResetPasswordLink( function( token, finished ) { | ||
28 | // expect( token ).to.equal( 'blah' ); | ||
29 | // finished(); | ||
30 | // done(); | ||
31 | // }); | ||
32 | // }); | ||
33 | }); | ||
34 | }); | ||
35 | 1 | describe( 'Reset Password', function() { |
tests/signup.js
1 | describe( 'Sign Up', function() { | 1 | /* eslint-env mocha */ |
2 | beforeEach( function() { | 2 | /* eslint-disable func-names, prefer-arrow-callback */ |
3 | server.execute( function() { | 3 | |
4 | var user = Meteor.users.findOne( { 'emails.address': 'carl.winslow@abc.com' } ); | 4 | describe('Sign Up', function () { |
5 | if ( user ) { | 5 | beforeEach(function () { |
6 | Meteor.users.remove( user._id ); | 6 | server.execute(function () { |
7 | const user = Meteor.users.findOne({ 'emails.address': 'carl.winslow@abc.com' }); | ||
8 | if (user) { | ||
9 | Meteor.users.remove(user._id); | ||
7 | } | 10 | } |
8 | }); | 11 | }); |
9 | }); | 12 | }); |
10 | 13 | ||
11 | it( 'should create a new user and login with redirect to index', function() { | 14 | it('should create a new user and login with redirect to index', function () { |
12 | browser.url( 'http://localhost:3000/signup' ) | 15 | browser.url('http://localhost:3000/signup') |
13 | .setValue( '[name="firstName"]', 'Carl' ) | 16 | .setValue('[name="firstName"]', 'Carl') |
14 | .setValue( '[name="lastName"]', 'Winslow' ) | 17 | .setValue('[name="lastName"]', 'Winslow') |
15 | .setValue( '[name="emailAddress"]', 'carl.winslow@abc.com' ) | 18 | .setValue('[name="emailAddress"]', 'carl.winslow@abc.com') |
16 | .setValue( '[name="password"]', 'bigguy1989' ) | 19 | .setValue('[name="password"]', 'bigguy1989') |
17 | .submitForm( 'form' ); | 20 | .submitForm('form'); |
18 | 21 | ||
19 | browser.waitForExist( '.jumbotron' ); | 22 | browser.waitForExist('.jumbotron'); |
20 | expect( browser.getUrl() ).to.equal( 'http://localhost:3000/' ); | 23 | expect(browser.getUrl()).to.equal('http://localhost:3000/'); |
21 | }); | 24 | }); |
22 | }); | 25 | }); |
23 | 26 |