From ad8543fe2f58f7b510f51f6080efc2f61b679aef Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Wed, 3 Oct 2018 13:54:17 +0200 Subject: [PATCH 01/13] Add babel test presets --- .babelrc | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.babelrc b/.babelrc index 4f95cb077..e7601125e 100644 --- a/.babelrc +++ b/.babelrc @@ -3,6 +3,14 @@ ["env", { "modules": false }] ], "plugins": [ - "syntax-dynamic-import" - ] + "syntax-dynamic-import", + "transform-runtime" + ], + "env": { + "test": { + "presets": [ + ["env", { "targets": { "node": "current" }}] + ] + } + } } From c0ccf67698ded9a8174e32ac56bfde733629294b Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Wed, 3 Oct 2018 13:58:23 +0200 Subject: [PATCH 02/13] Add jest config file --- jest.config.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 jest.config.js diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 000000000..58a20c4d2 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,25 @@ +module.exports = { + "moduleFileExtensions": [ + "js", + "json", + // tell Jest to handle *.vue files + "vue" + ], + "transform": { + // process js with babel-jest + "^.+\\.js$": "/node_modules/babel-jest", + // process *.vue files with vue-jest + ".*\\.(vue)$": "/node_modules/vue-jest" + }, + // support the same @ -> src alias mapping in source code + "moduleNameMapper": { + "^@/(.*)$": "/src/$1", + '../api': '/src/api/__mocks__/fake-api.js' + }, + // serializer for snapshots + "snapshotSerializers": [ + "/node_modules/jest-serializer-vue" + ], + "setupTestFrameworkScriptFile": "/src/jest-setup.js" + +} From b0b0902524ce5b351caf4a02e53ffab530aef934 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Wed, 3 Oct 2018 13:58:50 +0200 Subject: [PATCH 03/13] Add fake-api --- src/api/__mocks__/fake-api.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/api/__mocks__/fake-api.js diff --git a/src/api/__mocks__/fake-api.js b/src/api/__mocks__/fake-api.js new file mode 100644 index 000000000..df401a379 --- /dev/null +++ b/src/api/__mocks__/fake-api.js @@ -0,0 +1,24 @@ +import { addedItemId, fakeItemList, fakeUser, newItemListAfterAddingNewItem } from '../../../test/fake-data' + +export function fetchIdsByType(type) { + return Promise.resolve(Object.keys(fakeItemList)) +} + +export function fetchItem (id) { + return Promise.resolve(fakeItemList[id]) +} + +export function watchList (type, cb) { + cb(newItemListAfterAddingNewItem) +} + +export function fetchItems (ids) { + return Promise.all(ids.map(id => fetchItem(id))) +} + +export function fetchUser (id) { + if (id === fakeUser.id) return Promise.resolve(fakeUser) + return Promise.reject('User not found') +} + + From 2d707a46aac90640ca2a1fc3da88bf663ae73492 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Wed, 3 Oct 2018 14:10:27 +0200 Subject: [PATCH 04/13] Add fake data --- test/fake-data.js | 17 + test/fake-news.json | 1288 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1305 insertions(+) create mode 100644 test/fake-data.js create mode 100644 test/fake-news.json diff --git a/test/fake-data.js b/test/fake-data.js new file mode 100644 index 000000000..79ac4ad07 --- /dev/null +++ b/test/fake-data.js @@ -0,0 +1,17 @@ +import news from './fake-news.json' + +const arrayToObject = (array) => + array.reduce((obj, item) => { + obj[item.id] = item + return obj + }, {}) + +export const fakeItemList = arrayToObject(news) + +const anItemId = 17944752 +export const fakeItem = fakeItemList[anItemId] + +export const fakeUser = { id: 17944752} + +export const addedItemId = 17938548 +export const newItemListAfterAddingNewItem = [ 1, 2, 3, 4, 5, addedItemId, 6, 7, 8, 9 ] diff --git a/test/fake-news.json b/test/fake-news.json new file mode 100644 index 000000000..fc10c6210 --- /dev/null +++ b/test/fake-news.json @@ -0,0 +1,1288 @@ +[ + { + "by": "jxub", + "descendants": 26, + "id": 17946047, + "kids": [ + 17946331, + 17946193, + 17946235, + 17946261, + 17946210, + 17946305, + 17946140, + 17946163, + 17946348, + 17946303 + ], + "score": 66, + "time": 1536503622, + "title": "Clojure is cool", + "type": "story", + "url": "http://ahungry.com/blog/2018-09-09-Clojure-is-Cool.html", + "__lastUpdated": 1536507786019 + }, + { + "by": "anonu", + "descendants": 31, + "id": 17945377, + "kids": [ + 17945434, + 17946076, + 17945490, + 17945403, + 17945844, + 17945835 + ], + "score": 95, + "time": 1536491543, + "title": "Isochronous Curves", + "type": "story", + "url": "https://en.wikipedia.org/wiki/Tautochrone_curve", + "__lastUpdated": 1536507786026 + }, + { + "by": "fanf2", + "descendants": 62, + "id": 17944991, + "kids": [ + 17945913, + 17945164, + 17945492, + 17945323, + 17946180, + 17945300, + 17945430, + 17945511, + 17945175, + 17945982, + 17945586 + ], + "score": 172, + "time": 1536482584, + "title": "First-party isolation in Firefox: what breaks if you enable it?", + "type": "story", + "url": "https://www.ctrl.blog/entry/firefox-fpi", + "__lastUpdated": 1536507786027 + }, + { + "by": "grammers", + "descendants": 24, + "id": 17945116, + "kids": [ + 17945785, + 17945614, + 17946100, + 17945798, + 17946179, + 17946115, + 17945694, + 17945786 + ], + "score": 104, + "time": 1536485454, + "title": "How Tutanota replaced Google’s FCM with their own notification system", + "type": "story", + "url": "https://f-droid.org/en/2018/09/03/replacing-gcm-in-tutanota.html", + "__lastUpdated": 1536507786028 + }, + { + "by": "chriskanan", + "descendants": 23, + "id": 17945648, + "kids": [ + 17946366, + 17945731, + 17945738, + 17945921, + 17946083, + 17946012 + ], + "score": 70, + "time": 1536497050, + "title": "Infectious Theory of Alzheimer's Disease Draws Fresh Interest", + "type": "story", + "url": "https://www.npr.org/sections/health-shots/2018/09/09/645629133/infectious-theory-of-alzheimers-disease-draws-fresh-interest", + "__lastUpdated": 1536507786029 + }, + { + "by": "ColinWright", + "descendants": 6, + "id": 17945507, + "kids": [ + 17946149, + 17946006, + 17945884 + ], + "score": 23, + "time": 1536494565, + "title": "Spontaneous knotting of an agitated string (2007)", + "type": "story", + "url": "https://www.ncbi.nlm.nih.gov/pmc/articles/PMC2034230/", + "__lastUpdated": 1536507786030 + }, + { + "by": "mooreds", + "descendants": 37, + "id": 17945633, + "kids": [ + 17946058, + 17945954, + 17946053, + 17946052, + 17945959, + 17946284, + 17945818 + ], + "score": 55, + "time": 1536496894, + "title": "Beijing’s Three Options: Unemployment, Debt, or Wealth Transfers", + "type": "story", + "url": "https://carnegieendowment.org/chinafinancialmarkets/77178", + "__lastUpdated": 1536507786031 + }, + { + "by": "Tomte", + "descendants": 44, + "id": 17944656, + "kids": [ + 17946086, + 17945157, + 17945701, + 17945621, + 17946247, + 17945500, + 17945292, + 17945649 + ], + "score": 91, + "time": 1536474845, + "title": "To Restore Civil Society, Start with the Library", + "type": "story", + "url": "https://www.nytimes.com/2018/09/08/opinion/sunday/civil-society-library.html", + "__lastUpdated": 1536507786031 + }, + { + "by": "pgen", + "descendants": 3, + "id": 17944954, + "kids": [ + 17945262 + ], + "score": 79, + "time": 1536481786, + "title": "Smenu, a command-line advanced selection filter and a menu builder for terminal", + "type": "story", + "url": "https://github.com/p-gen/smenu", + "__lastUpdated": 1536507786032 + }, + { + "by": "amckenzie", + "id": 17946246, + "score": 1, + "time": 1536505836, + "title": "Rescale Is Hiring Senior Back End Engineers in San Francisco", + "type": "job", + "url": "https://jobs.lever.co/rescale/ba8800d3-b0bd-40b0-8a72-887e27904553?lever-origin=applied&lever-source%5B%5D=Hacker%20News", + "__lastUpdated": 1536507786033 + }, + { + "by": "kiyanwang", + "descendants": 0, + "id": 17946230, + "score": 10, + "time": 1536505639, + "title": " EU copyright must protect access to knowledge and the commons", + "type": "story", + "url": "https://creativecommons.org/2018/09/07/its-now-or-never-eu-copyright-must-protect-access-to-knowledge-and-the-commons/", + "__lastUpdated": 1536507786034 + }, + { + "by": "jrepinc", + "descendants": 1, + "id": 17946145, + "kids": [ + 17946363 + ], + "score": 16, + "time": 1536504845, + "title": "GNU nano 3.0 released", + "type": "story", + "url": "http://lists.gnu.org/archive/html/info-nano/2018-09/msg00000.html", + "__lastUpdated": 1536507786034 + }, + { + "by": "privong", + "descendants": 14, + "id": 17945233, + "kids": [ + 17946044, + 17946240, + 17945728, + 17945512, + 17945495, + 17945485, + 17945698 + ], + "score": 28, + "time": 1536488561, + "title": "How social-media platforms dispense justice", + "type": "story", + "url": "https://www.economist.com/business/2018/09/08/how-social-media-platforms-dispense-justice", + "__lastUpdated": 1536507786035 + }, + { + "by": "beliu", + "descendants": 20, + "id": 17944296, + "kids": [ + 17946042, + 17946089, + 17944649, + 17945365, + 17944976, + 17945744, + 17945085, + 17944685, + 17944943, + 17945910, + 17945919 + ], + "score": 148, + "time": 1536466136, + "title": "Mistakes C/C++ Devs make writing Go", + "type": "story", + "url": "https://about.sourcegraph.com/go/gophercon-2018-5-mistakes-c-c-devs-make-writing-go", + "__lastUpdated": 1536507786036 + }, + { + "by": "ehudla", + "descendants": 9, + "id": 17945756, + "kids": [ + 17946242, + 17946197, + 17946092, + 17946074, + 17946054, + 17946123 + ], + "score": 42, + "time": 1536498754, + "title": "Isaac Asimov on The David Letterman Show (1980) [video]", + "type": "story", + "url": "https://www.youtube.com/watch?v=365kJOsFd3w", + "__lastUpdated": 1536507786037 + }, + { + "by": "cube2222", + "descendants": 4, + "id": 17945090, + "kids": [ + 17945436 + ], + "score": 35, + "time": 1536484931, + "title": "Porcupine: A fast linearizability checker written in Go", + "type": "story", + "url": "https://github.com/anishathalye/porcupine/blob/master/README.md", + "__lastUpdated": 1536507786037 + }, + { + "by": "petethomas", + "descendants": 4, + "id": 17939679, + "kids": [ + 17946233, + 17946322 + ], + "score": 16, + "time": 1536387024, + "title": "Mezcal Is Born of Time, Tradition and a Slow-Growing Plant", + "type": "story", + "url": "https://www.nytimes.com/2018/09/06/travel/oaxacas-potent-secret-mezcal-is-born-of-time-tradition-and-a-slow-growing-plant.html", + "__lastUpdated": 1536507786038 + }, + { + "by": "synesso", + "descendants": 11, + "id": 17940516, + "kids": [ + 17944730, + 17944719, + 17944692, + 17944742 + ], + "score": 84, + "time": 1536407989, + "title": "DIY puller RC flying wing airplane with Kline–Fogleman modified airfoil", + "type": "story", + "url": "http://ausleuchtung.ch/kfm2/kfm2-puller.php", + "__lastUpdated": 1536507786038 + }, + { + "by": "f_allwein", + "descendants": 3, + "id": 17944934, + "kids": [ + 17945618 + ], + "score": 42, + "time": 1536481189, + "title": "UK’s worst-selling map: The empty landscape charted by OS440", + "type": "story", + "url": "https://www.theguardian.com/uk-news/2018/sep/09/uk-worst-selling-map-empty-landscape-ordnance-survey-os440-glen-cassley", + "__lastUpdated": 1536507786039 + }, + { + "by": "peter_d_sherman", + "descendants": 26, + "id": 17944882, + "kids": [ + 17945978, + 17945683, + 17945763, + 17945420 + ], + "score": 31, + "time": 1536480187, + "title": "More Companies That No Longer Require a Degree", + "type": "story", + "url": "https://www.glassdoor.com/blog/no-degree-required/", + "__lastUpdated": 1536507786040 + }, + { + "by": "tosh", + "descendants": 150, + "id": 17943076, + "kids": [ + 17943233, + 17943267, + 17943472, + 17945337, + 17945418, + 17945534, + 17944596, + 17945184, + 17943643, + 17943412, + 17945761, + 17944440, + 17945156, + 17944509, + 17943795, + 17945110, + 17944687, + 17944447, + 17943829, + 17943271, + 17943394, + 17944914, + 17944496, + 17944050, + 17943241, + 17943182, + 17945589, + 17943190 + ], + "score": 510, + "time": 1536444784, + "title": "XSV: A fast CSV command-line toolkit written in Rust", + "type": "story", + "url": "https://github.com/BurntSushi/xsv", + "__lastUpdated": 1536507801895 + }, + { + "by": "ScottWRobinson", + "descendants": 1, + "id": 17944005, + "kids": [ + 17946177 + ], + "score": 3, + "time": 1536460327, + "title": "Into a new era by Desire", + "type": "story", + "url": "https://www.pouet.net/prod.php?which=78044", + "__lastUpdated": 1536507801913 + }, + { + "by": "joegahona", + "descendants": 43, + "id": 17942237, + "kids": [ + 17942588, + 17945627, + 17945931, + 17945805, + 17945935, + 17945599, + 17945656, + 17945661, + 17942359, + 17946296, + 17946157, + 17945778, + 17945970, + 17942822, + 17945723, + 17945846, + 17942407 + ], + "score": 52, + "time": 1536433324, + "title": "Why Are Newspaper Websites So Horrible?", + "type": "story", + "url": "https://www.citylab.com/life/2018/04/why-are-local-newspaper-websites-so-horrible/558152/", + "__lastUpdated": 1536507801914 + }, + { + "by": "sahin-boydas", + "descendants": 222, + "id": 17940943, + "kids": [ + 17941564, + 17942099, + 17941527, + 17944415, + 17941626, + 17941902, + 17943249, + 17941713, + 17944165, + 17942118, + 17941506, + 17941925, + 17942339, + 17941599, + 17944407, + 17942131, + 17941734, + 17941437, + 17943628, + 17942179, + 17943631, + 17941649, + 17943261, + 17944885, + 17941645, + 17941446, + 17941555, + 17942647, + 17942416, + 17941534, + 17943395, + 17941549, + 17941859, + 17943133 + ], + "score": 778, + "time": 1536416804, + "title": "A year later, Equifax has faced little fallout from losing data", + "type": "story", + "url": "https://techcrunch.com/2018/09/08/equifax-one-year-later-unscathed/", + "__lastUpdated": 1536507801915 + }, + { + "by": "chris408", + "descendants": 7, + "id": 17944402, + "kids": [ + 17945050, + 17945062 + ], + "score": 44, + "time": 1536468050, + "title": "Certificate transparency logs and how they are a gold mine to bug hunters", + "type": "story", + "url": "https://chris408.com/post/certificate-transparency-logs-and-how-they-are-a-gold-mine-to-bug-hunters/", + "__lastUpdated": 1536507801916 + }, + { + "by": "colejohnson66", + "descendants": 0, + "id": 17946234, + "score": 6, + "time": 1536505665, + "title": "NSA metadata program “consistent” with Fourth Amendment, Kavanaugh once argued", + "type": "story", + "url": "https://arstechnica.com/tech-policy/2018/09/even-after-nsa-metadata-program-revised-kavanaugh-argued-in-favor-of-it/", + "__lastUpdated": 1536507801917 + }, + { + "by": "haywirez", + "descendants": 3, + "id": 17939965, + "kids": [ + 17940801, + 17945496 + ], + "score": 18, + "time": 1536393044, + "title": "Hacking Soundcloud: Creating an Interactive Track (2017)", + "type": "story", + "url": "https://haywirez.com/hacking-soundcloud/", + "__lastUpdated": 1536507801918 + }, + { + "by": "rishabhd", + "descendants": 1, + "id": 17939911, + "kids": [ + 17946327 + ], + "score": 10, + "time": 1536391978, + "title": "The Scientific Intelligencer (1993)", + "type": "story", + "url": "https://www.cia.gov/library/center-for-the-study-of-intelligence/kent-csi/vol6no4/html/v06i4a05p_0001.htm", + "__lastUpdated": 1536507801919 + }, + { + "by": "10000100001010", + "descendants": 5, + "id": 17943961, + "kids": [ + 17945494, + 17945543, + 17945971, + 17944242 + ], + "score": 11, + "time": 1536459431, + "title": "Exploring the Worst Case Complexity of Quicksort (2015)", + "type": "story", + "url": "https://tyler-davis.com/post/quicksort-randomize-worst-case/", + "__lastUpdated": 1536507801919 + }, + { + "by": "DyslexicAtheist", + "descendants": 0, + "id": 17940229, + "score": 30, + "time": 1536399711, + "title": "European observatory on Algorithmic Sovereignty", + "type": "story", + "url": "http://algosov.org/", + "__lastUpdated": 1536507801920 + }, + { + "by": "vq", + "descendants": 7, + "id": 17945100, + "kids": [ + 17945491, + 17946349 + ], + "score": 53, + "time": 1536485106, + "title": "Why I never finish my Haskell programs (part 2)", + "type": "story", + "url": "https://blog.plover.com/prog/haskell/what-goes-wrong-2.html", + "__lastUpdated": 1536507801921 + }, + { + "by": "rayascott", + "descendants": 76, + "id": 17940172, + "kids": [ + 17944329, + 17942981, + 17945609, + 17943606, + 17943466, + 17945581, + 17945769, + 17943478, + 17943337, + 17942712, + 17943596, + 17944004, + 17942764, + 17943341, + 17944514, + 17945206, + 17943230, + 17943121, + 17943067, + 17942726, + 17943023 + ], + "score": 471, + "time": 1536398240, + "title": "Tools of the Trade, from Hacker News", + "type": "story", + "url": "https://github.com/cjbarber/ToolsOfTheTrade", + "__lastUpdated": 1536507801922 + }, + { + "by": "e15ctr0n", + "descendants": 13, + "id": 17943943, + "kids": [ + 17944824, + 17944021, + 17945003, + 17944491, + 17944841 + ], + "score": 38, + "time": 1536459133, + "title": "An Inverted Jenny Surfaces", + "type": "story", + "url": "https://www.nytimes.com/2018/09/06/nyregion/inverted-jenny-stamp.html", + "__lastUpdated": 1536507801923 + }, + { + "by": "wheresvic1", + "descendants": 0, + "id": 17945431, + "score": 3, + "time": 1536493058, + "title": "Lisp for the Web", + "type": "story", + "url": "https://www.adamtornhill.com/articles/lispweb.htm", + "__lastUpdated": 1536507801924 + }, + { + "by": "pisky", + "descendants": 130, + "id": 17944311, + "kids": [ + 17945060, + 17944933, + 17945684, + 17944707, + 17944853, + 17944785, + 17945011, + 17944966, + 17945590, + 17944784, + 17945040, + 17946078, + 17945524, + 17945007, + 17945576, + 17944887, + 17945347, + 17945196, + 17945957, + 17945072, + 17945720, + 17945052, + 17944994, + 17944940, + 17945073, + 17944611, + 17944748, + 17944552, + 17945215, + 17944718, + 17944807, + 17944833, + 17944724, + 17945966, + 17945339, + 17944852, + 17944938, + 17945155 + ], + "score": 237, + "text": "Long story short: bought a couple of Chromebooks over the years (as they're nice multi user machines), created Google accounts on each but never gave a phone number. Now after years of use, Google pops up an "unrecognized device" roadblock AFTER I enter the password to log in, with the message "enter a phone number to get a text message with a verification code".

There is no mention of suspicious activity. The only trigger I can think of is a recent modem reset that changed my Public IP, and my new IP doesn't appear to resolve to my old physical location in Google's geoip db.

Am I crazy or does this seem like an extremely cynical attempt to get more phone numbers? I don't even understand how giving them my phone number proves anything as I definitely did not ever give them one previously.

Unfortunately burner phones are not available in my country, so that's not an option.", + "time": 1536466378, + "title": "Tell HN: Google requiring phone number to log into Chromebook", + "type": "story", + "__lastUpdated": 1536507801925 + }, + { + "by": "kiyanwang", + "descendants": 1, + "id": 17944752, + "kids": [ + 17944825, + 17945470 + ], + "score": 9, + "time": 1536477028, + "title": "Making the Case Against Google AMP", + "type": "story", + "url": "https://www.macobserver.com/link/making-case-against-google-amp/", + "__lastUpdated": 1536507801926 + }, + { + "by": "edward", + "descendants": 18, + "id": 17943030, + "kids": [ + 17944200, + 17944537, + 17944834, + 17943139 + ], + "score": 47, + "time": 1536444088, + "title": "USB drives with no phantom load", + "type": "story", + "url": "http://joeyh.name/blog/entry/usb_drives_with_no_phantom_load/", + "__lastUpdated": 1536507801926 + }, + { + "by": "tareqak", + "descendants": 231, + "id": 17938548, + "kids": [ + 17938681, + 17938967, + 17939033, + 17938659, + 17938733, + 17940017, + 17940982, + 17939229, + 17939130, + 17939846, + 17941277, + 17939601, + 17940244, + 17940888, + 17938781, + 17938729, + 17940893, + 17941843, + 17938907, + 17940724, + 17945081, + 17942279, + 17938975, + 17939224, + 17939066, + 17943473, + 17941324, + 17942987, + 17940689, + 17939681, + 17940656, + 17939762, + 17939635, + 17939829, + 17939854, + 17938678, + 17938912, + 17941251, + 17939237 + ], + "score": 640, + "time": 1536364721, + "title": "Popular iPhone apps caught sending user location data to monetization firms", + "type": "story", + "url": "https://techcrunch.com/2018/09/07/a-dozen-popular-iphone-apps-caught-quietly-sending-user-locations-to-monetization-firms/", + "__lastUpdated": 1536507801927 + }, + { + "by": "joeyespo", + "descendants": 99, + "id": 17942771, + "kids": [ + 17945915, + 17944979, + 17944236, + 17944794, + 17944130, + 17945317, + 17944806, + 17945179, + 17944952, + 17944960, + 17944456, + 17945143, + 17944762, + 17944594, + 17945033, + 17943933, + 17944150 + ], + "score": 146, + "time": 1536440660, + "title": "Loneliness is a serious public-health problem", + "type": "story", + "url": "https://www.economist.com/international/2018/09/01/loneliness-is-a-serious-public-health-problem", + "__lastUpdated": 1536507801928 + }, + { + "by": "tomohawk", + "descendants": 6, + "id": 17945560, + "kids": [ + 17945937 + ], + "score": 8, + "time": 1536495509, + "title": "Georgia Rancher Wins Compensation for Bald Eagle Attacks", + "type": "story", + "url": "https://www.ajc.com/blog/news-to-me/georgia-rancher-wins-compensation-bald-eagle-attacks/mMotH8bCF2Xptra86hJ9CP/", + "__lastUpdated": 1536507801929 + }, + { + "by": "victorkab", + "descendants": 0, + "id": 17944455, + "kids": [ + 17945354 + ], + "score": 14, + "time": 1536469120, + "title": "A New Challenger to Equifax’s Employee Verification Service", + "type": "story", + "url": "https://www.nytimes.com/2018/04/11/your-money/equifax-challenger-truework.html", + "__lastUpdated": 1536507820677 + }, + { + "by": "wheresvic1", + "descendants": 104, + "id": 17943405, + "kids": [ + 17943990, + 17943932, + 17945020, + 17943914, + 17945194, + 17944073, + 17944326, + 17945059, + 17945128, + 17943973, + 17944881, + 17944717, + 17944177 + ], + "score": 157, + "time": 1536449707, + "title": "Time to look beyond Oracle's JDK", + "type": "story", + "url": "https://blog.joda.org/2018/09/time-to-look-beyond-oracles-jdk.html", + "__lastUpdated": 1536507820684 + }, + { + "by": "dsr12", + "descendants": 136, + "id": 17941055, + "kids": [ + 17942276, + 17944196, + 17942250, + 17941800, + 17942775, + 17942372, + 17942130, + 17943221, + 17942928, + 17944274, + 17944047, + 17942661, + 17945107, + 17942158, + 17942201, + 17942779, + 17943372, + 17943097, + 17942330, + 17944201, + 17942064, + 17942080, + 17942228, + 17943441, + 17942629 + ], + "score": 192, + "time": 1536418409, + "title": "OxyContin billionaire has patented a drug to wean addicts from opioids", + "type": "story", + "url": "https://www.washingtonpost.com/news/business/wp/2018/09/08/the-man-who-made-billions-of-dollars-from-oxycontin-is-pushing-a-drug-to-wean-addicts-off-opioids", + "__lastUpdated": 1536507820685 + }, + { + "by": "kanishkalinux", + "descendants": 38, + "id": 17942032, + "kids": [ + 17945400, + 17942474, + 17942824, + 17942837, + 17945353, + 17942678, + 17942873, + 17945452, + 17945288, + 17943689, + 17943349, + 17944781 + ], + "score": 176, + "time": 1536430718, + "title": "Show HN: Reminiscence, self-hosted bookmark manager and personal wayback machine", + "type": "story", + "url": "https://github.com/kanishka-linux/reminiscence", + "__lastUpdated": 1536507820686 + }, + { + "by": "tzhenghao", + "descendants": 1, + "id": 17942816, + "kids": [ + 17944142 + ], + "score": 34, + "time": 1536441229, + "title": "Reverse Engineering Firmware Primer (2012)", + "type": "story", + "url": "https://wiki.securityweekly.com/Reverse_Engineering_Firmware_Primer", + "__lastUpdated": 1536507820687 + }, + { + "by": "alex_hirner", + "descendants": 35, + "id": 17942112, + "kids": [ + 17943253, + 17945247, + 17944464, + 17943685, + 17942781, + 17943313, + 17943780 + ], + "score": 146, + "time": 1536431747, + "title": "The algebra and calculus of algebraic data types", + "type": "story", + "url": "https://codewords.recurse.com/issues/three/algebra-and-calculus-of-algebraic-data-types", + "__lastUpdated": 1536507820688 + }, + { + "by": "Tomte", + "descendants": 3, + "id": 17939774, + "kids": [ + 17945205, + 17945505 + ], + "score": 22, + "time": 1536389183, + "title": "Photojournalism with a Point and Shoot Camera (2011)", + "type": "story", + "url": "http://www.zoriah.net/blog/2011/04/photojournalism-with-a-point-and-shoot-camera-becoming-a-photojournalis-on-a-budget.html", + "__lastUpdated": 1536507820688 + }, + { + "by": "walterbell", + "descendants": 104, + "id": 17941835, + "kids": [ + 17943047, + 17943396, + 17943657, + 17942839, + 17943355, + 17942384, + 17943773, + 17942424, + 17942628, + 17942503, + 17942363, + 17943451, + 17943136, + 17943009, + 17943967, + 17944715, + 17942727, + 17942476, + 17942471, + 17943058, + 17943148 + ], + "score": 143, + "time": 1536428162, + "title": "Forgotten Greats of Science Fiction", + "type": "story", + "url": "https://www.tor.com/2018/09/04/who-are-the-forgotten-greats-of-science-fiction/", + "__lastUpdated": 1536507820692 + }, + { + "by": "pmcpinto", + "descendants": 114, + "id": 17942744, + "kids": [ + 17946350, + 17943806, + 17944612, + 17944900, + 17944167, + 17943928, + 17946302, + 17944648, + 17944324, + 17943902, + 17944343, + 17943737, + 17946104, + 17944078, + 17944962, + 17943972, + 17943844, + 17944764, + 17944451, + 17945146, + 17943981, + 17944671, + 17944431, + 17944442, + 17944414, + 17943837, + 17944170, + 17944138 + ], + "score": 152, + "time": 1536440210, + "title": "Sperm Count Zero", + "type": "story", + "url": "https://www.gq.com/story/sperm-count-zero", + "__lastUpdated": 1536507820693 + }, + { + "by": "MarkMc", + "descendants": 107, + "id": 17940929, + "kids": [ + 17941568, + 17941345, + 17942392, + 17941538, + 17941518, + 17941513, + 17941517, + 17941420, + 17942440, + 17941802, + 17945425, + 17941474, + 17942220, + 17941725, + 17942494, + 17941607, + 17941692, + 17941418, + 17943217, + 17941478, + 17941822, + 17943797, + 17942011, + 17941288, + 17941507, + 17941752, + 17941500, + 17941786, + 17945079, + 17943656 + ], + "score": 177, + "time": 1536416472, + "title": "Can good teaching be taught?", + "type": "story", + "url": "https://www.nytimes.com/interactive/2018/09/06/magazine/student-performance-atlanta-teaching.html", + "__lastUpdated": 1536507820694 + }, + { + "by": "PretzelFisch", + "descendants": 0, + "id": 17945797, + "score": 3, + "time": 1536499639, + "title": "Why Ghost is not Medium", + "type": "story", + "url": "http://scripting.com/2018/09/09/130344.html", + "__lastUpdated": 1536507820695 + }, + { + "by": "DrJaws", + "descendants": 25, + "id": 17944235, + "kids": [ + 17944716, + 17944736, + 17944571, + 17944576, + 17944572, + 17944694 + ], + "score": 65, + "time": 1536464557, + "title": "Top cancer researcher fails to disclose corporate ties in major journals", + "type": "story", + "url": "https://www.nytimes.com/2018/09/08/health/jose-baselga-cancer-memorial-sloan-kettering.html", + "__lastUpdated": 1536507820696 + }, + { + "by": "mercutio2", + "descendants": 141, + "id": 17938885, + "kids": [ + 17943073, + 17943099, + 17943971, + 17944280, + 17943154, + 17943452, + 17945029, + 17942982, + 17943289, + 17943150, + 17943142, + 17944507, + 17943254, + 17943187, + 17943117 + ], + "score": 96, + "time": 1536370445, + "title": "Poaching Tesla", + "type": "story", + "url": "https://www.aboveavalon.com/notes/2018/9/6/poaching-tesla", + "__lastUpdated": 1536507820696 + }, + { + "by": "tysone", + "descendants": 119, + "id": 17938262, + "kids": [ + 17940725, + 17940919, + 17938549, + 17938523, + 17944640, + 17939943, + 17939491, + 17941852, + 17939076, + 17938430, + 17939752, + 17941523, + 17939708, + 17938387 + ], + "score": 380, + "time": 1536360878, + "title": "Jack Ma to Retire from Alibaba", + "type": "story", + "url": "https://www.nytimes.com/2018/09/07/technology/alibaba-jack-ma-retiring.html", + "__lastUpdated": 1536507820697 + }, + { + "by": "matt_d", + "descendants": 39, + "id": 17939537, + "kids": [ + 17944779, + 17944127, + 17944109, + 17944677, + 17945806, + 17943931, + 17944173 + ], + "score": 81, + "time": 1536383731, + "title": "Checked C: Making C Safer by Extension", + "type": "story", + "url": "https://www.microsoft.com/en-us/research/publication/checkedc-making-c-safe-by-extension/", + "__lastUpdated": 1536507820698 + }, + { + "by": "walterbell", + "descendants": 0, + "id": 17939338, + "score": 10, + "time": 1536379373, + "title": "One Blind Guy’s Experience with Android – How Accessible Is It Really? (2016)", + "type": "story", + "url": "https://mosen.org/6p/", + "__lastUpdated": 1536507820699 + }, + { + "by": "panic", + "descendants": 0, + "id": 17944800, + "score": 6, + "time": 1536478272, + "title": "APL matrix product operator", + "type": "story", + "url": "https://blog.plover.com/prog/apl-matrix-product.html", + "__lastUpdated": 1536507820701 + }, + { + "by": "MilnerRoute", + "descendants": 26, + "id": 17943754, + "kids": [ + 17945228, + 17944388, + 17944689, + 17944893, + 17944778, + 17944205, + 17944810, + 17944575, + 17944022, + 17944401 + ], + "score": 60, + "time": 1536455910, + "title": "The Website Obesity Crisis (2015)", + "type": "story", + "url": "http://idlewords.com/talks/website_obesity.htm", + "__lastUpdated": 1536507820702 + }, + { + "by": "patagonia", + "descendants": 41, + "id": 17944553, + "kids": [ + 17945929, + 17945752, + 17945946, + 17945445, + 17945632, + 17945464, + 17945603, + 17945360 + ], + "score": 33, + "time": 1536471989, + "title": "A review of the Julia language (2014)", + "type": "story", + "url": "https://danluu.com/julialang/", + "__lastUpdated": 1536507820703 + }, + { + "by": "kedmi", + "descendants": 67, + "id": 17942895, + "kids": [ + 17944349, + 17945383, + 17944072, + 17943589, + 17944423, + 17944562, + 17944294, + 17943803 + ], + "score": 62, + "time": 1536442406, + "title": "DNS over TLS – Thoughts and Implementation", + "type": "story", + "url": "https://sagi.io/2018/09/dns-over-tls---thoughts-and-implementation/index.html", + "__lastUpdated": 1536507820704 + } +] \ No newline at end of file From aae397f5e6db426153761e7d308490141b189b22 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Wed, 3 Oct 2018 14:10:47 +0200 Subject: [PATCH 05/13] Add method to resolve promises under test --- test/test-utils.js | 1 + 1 file changed, 1 insertion(+) create mode 100644 test/test-utils.js diff --git a/test/test-utils.js b/test/test-utils.js new file mode 100644 index 000000000..1271751bb --- /dev/null +++ b/test/test-utils.js @@ -0,0 +1 @@ +export const resolvePromises = () => new Promise(resolve => setImmediate(resolve)) From 269c680b1018e6678e09b89ca0aee1068dca3e0e Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Wed, 3 Oct 2018 14:11:10 +0200 Subject: [PATCH 06/13] Add global jest-setup --- src/jest-setup.js | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/jest-setup.js diff --git a/src/jest-setup.js b/src/jest-setup.js new file mode 100644 index 000000000..ca8db8b61 --- /dev/null +++ b/src/jest-setup.js @@ -0,0 +1,9 @@ +import Vue from 'vue' +import * as filters from './util/filters' + +// We would extract this to a function that would be reused by both app.js and jest-setup but, +// we didn't want to change original production code +Object.keys(filters).forEach(key => { + Vue.filter(key, filters[key]) +}) + From dde5059eed39e32abe32126c648410589834c714 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Wed, 3 Oct 2018 14:14:43 +0200 Subject: [PATCH 07/13] Add test dependencies --- package.json | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 625372581..22dcfacb6 100644 --- a/package.json +++ b/package.json @@ -4,11 +4,12 @@ "author": "Evan You ", "private": true, "scripts": { - "dev": "node server", + "dev": "cross-env PORT=8888 node server", "start": "cross-env NODE_ENV=production node server", "build": "rimraf dist && npm run build:client && npm run build:server", "build:client": "cross-env NODE_ENV=production webpack --config build/webpack.client.config.js --progress --hide-modules", - "build:server": "cross-env NODE_ENV=production webpack --config build/webpack.server.config.js --progress --hide-modules" + "build:server": "cross-env NODE_ENV=production webpack --config build/webpack.server.config.js --progress --hide-modules", + "test": "jest -c jest.config.js" }, "engines": { "node": ">=7.0", @@ -31,20 +32,26 @@ "vuex-router-sync": "^5.0.0" }, "devDependencies": { + "@babel/plugin-transform-runtime": "^7.0.0", + "@vue/test-utils": "^1.0.0-beta.24", "autoprefixer": "^7.1.6", "babel-core": "^6.26.0", "babel-loader": "^7.1.2", "babel-plugin-syntax-dynamic-import": "^6.18.0", + "babel-plugin-transform-runtime": "^6.23.0", "babel-preset-env": "^1.6.1", "chokidar": "^1.7.0", "css-loader": "^0.28.7", "file-loader": "^1.1.5", "friendly-errors-webpack-plugin": "^1.6.1", + "jest": "^23.5.0", + "jest-serializer-vue": "^2.0.2", "rimraf": "^2.6.2", "stylus": "^0.54.5", "stylus-loader": "^3.0.1", "sw-precache-webpack-plugin": "^0.11.4", "url-loader": "^0.6.2", + "vue-jest": "^2.6.0", "vue-loader": "^15.0.0-beta.1", "vue-template-compiler": "^2.5.16", "webpack": "^3.8.1", From 3ab56aeb0e73cae7b539ef916815e180983672eb Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Wed, 3 Oct 2018 14:15:19 +0200 Subject: [PATCH 08/13] Add UserView component unit tests --- src/views/__tests__/UserView.spec.js | 55 ++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/views/__tests__/UserView.spec.js diff --git a/src/views/__tests__/UserView.spec.js b/src/views/__tests__/UserView.spec.js new file mode 100644 index 000000000..5527ccfc2 --- /dev/null +++ b/src/views/__tests__/UserView.spec.js @@ -0,0 +1,55 @@ +import { mount } from '@vue/test-utils' +import { fakeUser } from '../../../test/fake-data' +import { resolvePromises } from '../../../test/test-utils' +import { createStore } from '../../store' +import UserView from '../UserView' + +let wrapper, store, route +describe('UserView.vue', () => { + + beforeEach(() => { + route = userRoute(fakeUser.id) + store = createStore() + }) + + it('Renders user id', async () => { + wrapper = await renderComponent(route) + expect(wrapper.text()).toContain(`User : ${fakeUser.id}`) + }) + + it('Renders time since creation', async () => { + fakeUser.created = new Date('September 07 2018')/1000 + Date.now = jest.fn(() => new Date('September 09 2018')) + + wrapper = await renderComponent(route) + + expect(wrapper.text()).toContain('2 days ago') + }) + + it('Calls the action to fetch the user by id', async () => { + const dispatchSpy = jest.spyOn(store, 'dispatch') + + await renderComponent(route) + + expect(dispatchSpy).toHaveBeenCalledWith('FETCH_USER', { id: fakeUser.id }) + expect(dispatchSpy.mock.calls.length).toBe(1) + }) +}) + +const userRoute = (id) => ({ + path: '/user', + params: { id } +}) + +const renderComponent = async route => { + const wrapper = mount(UserView, { store, + mocks: { + $route: route, + } + }) + + wrapper.vm.$options.asyncData({ store, route }) + await resolvePromises() + + return wrapper +} From d7e347c94202bce1b2b8b7b5b4d8c37042c10010 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Wed, 3 Oct 2018 14:15:40 +0200 Subject: [PATCH 09/13] Add ItemView component unit tests --- src/views/__tests__/ItemView.spec.js | 64 ++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/views/__tests__/ItemView.spec.js diff --git a/src/views/__tests__/ItemView.spec.js b/src/views/__tests__/ItemView.spec.js new file mode 100644 index 000000000..0e66dabb3 --- /dev/null +++ b/src/views/__tests__/ItemView.spec.js @@ -0,0 +1,64 @@ +import { mount } from '@vue/test-utils' +import { resolvePromises } from '../../../test/test-utils' +import { createStore } from '../../store' +import { fakeItem } from '../../../test/fake-data' +import ItemView from '../ItemView' + + +let wrapper, store +describe('ItemView.vue', () => { + + beforeEach(()=> { + store = createStore() + }) + + it('Renders item title', async () => { + wrapper = await renderComponent(fakeItem.id) + expect(wrapper.text()).toContain(fakeItem.title) + }) + + it('Renders item host', async () => { + fakeItem.url = 'https://www.fake.domain.com/link/fake-uri' + wrapper = await renderComponent(fakeItem.id) + expect(wrapper.text()).toContain('fake.domain.com') + }) + + it('Renders item user', async () => { + wrapper = await renderComponent(fakeItem.id) + expect(wrapper.text()).toContain('| by ' + fakeItem.by) + }) + + it('Calls the action to fetch the item by id', async () => { + const dispatchSpy = jest.spyOn(store, 'dispatch') + wrapper = renderComponent(fakeItem.id) + expect(dispatchSpy).toHaveBeenCalledWith('FETCH_ITEMS', { ids: [fakeItem.id] }) + }) + + it('Calls the action to fetch the comments by id', async () => { + const dispatchSpy = jest.spyOn(store, 'dispatch') + wrapper = await renderComponent(fakeItem.id) + expect(dispatchSpy).toHaveBeenCalledWith('FETCH_ITEMS', { ids: fakeItem.kids }) + }) +}) + +async function renderComponent(id) { + const route = { + path: '/item', + params: { id } + } + store.state.route = route + + const wrapper = mount(ItemView, { store, + mocks: { + $route: route, + }, + stubs: ['router-link'] + }) + + wrapper.vm.$options.asyncData({ store, route }) + await resolvePromises() + + return wrapper +} + + From 907f3471eacadc8ac6541af3171500c501315e1c Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Wed, 3 Oct 2018 14:16:26 +0200 Subject: [PATCH 10/13] Add ListView Component unit tests --- src/views/__tests__/CreateListView.spec.js | 108 +++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 src/views/__tests__/CreateListView.spec.js diff --git a/src/views/__tests__/CreateListView.spec.js b/src/views/__tests__/CreateListView.spec.js new file mode 100644 index 000000000..223121a57 --- /dev/null +++ b/src/views/__tests__/CreateListView.spec.js @@ -0,0 +1,108 @@ +import { mount } from '@vue/test-utils' +import { addedItemId, fakeItemList, newItemListAfterAddingNewItem } from '../../../test/fake-data' +import { resolvePromises } from '../../../test/test-utils' +import { createStore } from '../../store' +import CreateListView from '../CreateListView' + +const PAGE_TYPES = ['top', 'new', 'show', 'ask', 'job'] + +let wrapper, store, routerSpy +describe('CreateListView.vue', () => { + + beforeEach(()=> { + store = createStore() + routerSpy = jest.fn() + }) + + it('shows number of available pages', async () => { + const page = 1 + wrapper = await renderComponent('top', page) + + expect(wrapper.find('.news-list-nav').text()).toBe('< prev 1/3 more >') + }) + + it('shows current page in paginator', async () => { + const currentPage = 2 + wrapper = await renderComponent('top', currentPage) + + expect(wrapper.find('.news-list-nav').text()).toBe('< prev 2/3 more >') + }) + + PAGE_TYPES.forEach(async (type) => { + it('calls FETCH_LIST_DATA action for page ' + type, async () => { + const dispatchSpy = jest.spyOn(store, 'dispatch') + + wrapper = await renderComponent(type) + + expect(dispatchSpy).toHaveBeenCalledWith('FETCH_LIST_DATA', {"type": type}) + expect(dispatchSpy.mock.calls.length).toBe(3) + }) + }) + + it('loads 20 items', async () => { + wrapper = await renderComponent('top') + + expect(wrapper.findAll('.news-item')).toHaveLength(20) + }) + + describe('When new item is added in real time', ()=> { + + it('ENSURE_ACTIVE_ITEMS action is dispatched', async () => { + const dispatchSpy = jest.spyOn(store, 'dispatch') + + wrapper = await renderComponent('top') + + expect(dispatchSpy).toHaveBeenCalledWith('ENSURE_ACTIVE_ITEMS') + }) + + it('The new list is set', async () => { + const commitSpy = jest.spyOn(store, 'commit') + + wrapper = await renderComponent('top') + expect(commitSpy).toHaveBeenCalledWith('SET_LIST', {"ids": newItemListAfterAddingNewItem, "type": "top"}) + expect(wrapper.text()).toContain(fakeItemList[addedItemId].title) + }) + + it('The title of the new added item is rendered', async () => { + wrapper = await renderComponent('top') + + expect(wrapper.text()).toContain(fakeItemList[addedItemId].title) + }) + }) + +}) + +async function renderComponent(type, page) { + const $route = { + path: '/some/path', + params: { page } + } + store.state.route = $route + + const mixin = { + beforeMount: function () { + this.$root = { + _isMounted: true + } + } + } + + const wrapper = mount(CreateListView(type), { store, + propsData: { + type: 'type', + }, + mocks: { + $route, + $bar: { start: jest.fn(), finish: jest.fn() } + }, + stubs: ['router-link'], + mixins: [mixin] + }) + + wrapper.vm.$options.asyncData({ store }) + await resolvePromises() + + return wrapper +} + + From 49bc18d86e7203ab0b945efe43849ef7c34e5789 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Wed, 3 Oct 2018 14:21:15 +0200 Subject: [PATCH 11/13] Avoid using 8888 port in dev environment --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 22dcfacb6..1c52e2998 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "author": "Evan You ", "private": true, "scripts": { - "dev": "cross-env PORT=8888 node server", + "dev": "cross-env node server", "start": "cross-env NODE_ENV=production node server", "build": "rimraf dist && npm run build:client && npm run build:server", "build:client": "cross-env NODE_ENV=production webpack --config build/webpack.client.config.js --progress --hide-modules", From dce58399aa1056d50a0c01268a9303c1ac3c9da4 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Wed, 3 Oct 2018 15:28:39 +0200 Subject: [PATCH 12/13] Avoid cross-env in dev script --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1c52e2998..faa6663db 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "author": "Evan You ", "private": true, "scripts": { - "dev": "cross-env node server", + "dev": "node server", "start": "cross-env NODE_ENV=production node server", "build": "rimraf dist && npm run build:client && npm run build:server", "build:client": "cross-env NODE_ENV=production webpack --config build/webpack.client.config.js --progress --hide-modules", From c3492b10424486c88bb7a40e5f8e6f0d200c1cd6 Mon Sep 17 00:00:00 2001 From: Alberto Gualis Date: Wed, 31 Oct 2018 17:51:15 +0100 Subject: [PATCH 13/13] force ENV_NODE=test when runninghg jest --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index faa6663db..57a9ba7eb 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "build": "rimraf dist && npm run build:client && npm run build:server", "build:client": "cross-env NODE_ENV=production webpack --config build/webpack.client.config.js --progress --hide-modules", "build:server": "cross-env NODE_ENV=production webpack --config build/webpack.server.config.js --progress --hide-modules", - "test": "jest -c jest.config.js" + "test": "NODE_ENV=test jest -c jest.config.js" }, "engines": { "node": ">=7.0",