├── .appveyor.yml ├── .circleci └── config.yml ├── .gitignore ├── .travis.yml ├── LICENSE.md ├── README.md ├── images ├── advanced.png └── simple.png ├── index.html ├── package.json ├── renovate.json ├── src ├── cache.ts ├── colors.ts ├── github-repository.ts ├── index.d.ts ├── license.ts ├── repo.ts └── user.ts ├── test ├── data │ ├── abraham │ │ ├── empty.json │ │ ├── twitter-status.json │ │ └── twitteroauth.json │ ├── angular │ │ └── angular.json │ ├── error.json │ └── vuejs │ │ └── vue.json └── github-repository.test.ts ├── tsconfig.json └── tsconfig.production.json /.appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | nodejs_version: "9" 3 | 4 | install: 5 | - ps: Install-Product node $env:nodejs_version 6 | - npm install 7 | 8 | test_script: 9 | - node --version 10 | - npm --version 11 | - npm test 12 | 13 | cache: 14 | - node_modules 15 | - '%APPDATA%\npm-cache' 16 | 17 | build: off 18 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | macos: 5 | xcode: "11.3.1" 6 | steps: 7 | - checkout 8 | - run: brew cask install firefox google-chrome 9 | - run: node --version 10 | - run: npm --version 11 | - restore_cache: 12 | key: dependency-cache-{{ checksum "package.json" }} 13 | - run: npm install 14 | - save_cache: 15 | key: dependency-cache-{{ checksum "package.json" }} 16 | paths: 17 | - ./node_modules 18 | - run: npm run test 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | yarn.lock 4 | package-lock.json 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Work-around for https://github.com/travis-ci/travis-ci/issues/8836 2 | sudo: true 3 | language: node_js 4 | node_js: 5 | - node 6 | before_script: 7 | - node --version 8 | - npm --version 9 | - npm install 10 | script: 11 | - npm run test 12 | os: 13 | - linux 14 | cache: 15 | directories: 16 | - node_modules 17 | addons: 18 | firefox: latest 19 | chrome: stable 20 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2017 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | <github-repository> 2 | ==== 3 | 4 | [![Version Status](https://img.shields.io/npm/v/github-repository.svg?style=flat&label=version&colorB=4bc524)](https://npmjs.com/package/github-repository) 5 | [![macOS Build Status](https://img.shields.io/circleci/project/github/abraham/github-repository.svg?style=flat&label=macos)](https://circleci.com/gh/abraham/github-repository) 6 | [![Linux Build Status](https://img.shields.io/travis/abraham/github-repository.svg?style=flat&label=linux)](https://travis-ci.org/abraham/github-repository) 7 | [![Windows Build Status](https://img.shields.io/appveyor/ci/abraham/github-repository.svg?style=flat&label=windows)](https://ci.appveyor.com/project/abraham/github-repository) 8 | [![Dependency Status](https://david-dm.org/abraham/github-repository.svg?style=flat)](https://david-dm.org/abraham/github-repository) 9 | [![npm bundle size (minified + gzip)](https://img.shields.io/bundlephobia/minzip/github-repository.svg?style=flat&colorB=4bc524)](https://bundlephobia.com/result?p=github-repository) 10 | 11 | Install 12 | ---- 13 | 14 | Polyfill tags if you need them. This will include ShadowDOM and Custom Elements support. 15 | 16 | ``` 17 | 18 | ``` 19 | 20 | Loading this component. It would be a good idea to use a specific version instead of `latest`. 21 | 22 | ``` 23 | 24 | ``` 25 | 26 | Example 27 | ---- 28 | 29 | [Live demo](https://codepen.io/abraham/pen/PQoeqV) 30 | 31 | Usage 32 | ---- 33 | 34 | Set the `owner-repo` attribute to the `username/repository` of a GitHub repository. 35 | 36 | ``` 37 | 38 | ``` 39 | 40 | ![Example](/images/simple.png) 41 | 42 | For more advanced usage you can include: 43 | 44 | - `` to include a preview image 45 | - A number of `` to include status badges 46 | 47 | ``` 48 | 49 | Twitter Status embed preview 50 | 51 | 52 | Version Status 53 | 54 | 55 | macOS Build Status 56 | 57 | 58 | Linux Build Status 59 | 60 | 61 | Windows Build Status 62 | 63 | 64 | 65 | ``` 66 | 67 | ![Example with image and badges](/images/advanced.png) 68 | 69 | GitHub API Rate limit 70 | ---- 71 | 72 | This Web Component makes unauthenticated requests to the GitHub API. Since the GitHub API has a fairly restrictive 60 requests/hour per IP address, API responses are cached in `localStorage`. 73 | 74 | License 75 | ---- 76 | 77 | GithubRepository is released under an MIT license. 78 | 79 | Built, tested, and published with [Nutmeg](https://nutmeg.tools). 80 | -------------------------------------------------------------------------------- /images/advanced.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abraham/github-repository/a371d65236a3aec3de49568caa631a00d0145524/images/advanced.png -------------------------------------------------------------------------------- /images/simple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abraham/github-repository/a371d65236a3aec3de49568caa631a00d0145524/images/simple.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | github-repository demos 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 23 | 24 | 25 | 26 | 27 | 28 | Twitter Status embed preview 29 | Version Status 30 | macOS Build Status 31 | Linux Build Status 32 | Windows Build Status 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "github-repository", 3 | "version": "0.4.0", 4 | "description": "GitHub Repository Web Component", 5 | "main": "dist/github-repository.js", 6 | "module": "dist/github-repository.js", 7 | "unpkg": "dist/github-repository.min.js", 8 | "types": "dist/github-repository.d.ts", 9 | "directories": { 10 | "test": "test" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/abraham/github-repository.git" 15 | }, 16 | "engines": { 17 | "node": ">=8.0.0" 18 | }, 19 | "keywords": [ 20 | "github-repository", 21 | "github", 22 | "nutmeg", 23 | "web-component" 24 | ], 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/abraham/github-repository/issues" 28 | }, 29 | "homepage": "https://github.com/abraham/github-repository", 30 | "scripts": { 31 | "build": "npx nutmeg build .", 32 | "prebuild": "npx nutmeg clean .", 33 | "prepare": "npm run build -- --production", 34 | "pretest": "npm run build", 35 | "start": "npx nutmeg serve .", 36 | "test": "npx nutmeg test .", 37 | "watch": "npx nutmeg watch ." 38 | }, 39 | "dependencies": { 40 | "@nutmeg/seed": "0.17.0", 41 | "approximate-number": "2.1.0" 42 | }, 43 | "devDependencies": { 44 | "@nutmeg/cli": "0.17.0" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/cache.ts: -------------------------------------------------------------------------------- 1 | import { GithubRepository } from './github-repository'; 2 | import { RepoData } from './repo'; 3 | 4 | export class Cache { 5 | private readonly CACHE_LENGTH = 24 * 60 * 60 * 1000; 6 | private githubRepository: GithubRepository; 7 | 8 | constructor(githubRepository: GithubRepository) { 9 | this.githubRepository = githubRepository; 10 | } 11 | 12 | public get data(): RepoData | null { 13 | return this.cache.data; 14 | } 15 | 16 | public set data(data: RepoData | null) { 17 | if (!data) { return } 18 | const cache = { 19 | data: data, 20 | cachedAt: Date.now(), 21 | }; 22 | localStorage.setItem(this.cacheKey, JSON.stringify(cache)) 23 | } 24 | 25 | public get expired(): boolean { 26 | return this.cachedAt < Date.now() - this.CACHE_LENGTH; 27 | } 28 | 29 | private get cachedAt(): number { 30 | return this.cache.cachedAt || Date.now(); 31 | } 32 | 33 | private get cache(): RepoCache { 34 | const cache = localStorage.getItem(this.cacheKey); 35 | if (cache) { 36 | return JSON.parse(cache) as RepoCache; 37 | } else { 38 | return { 39 | cachedAt: 0, 40 | data: null, 41 | }; 42 | } 43 | } 44 | 45 | private get cacheKey(): string { 46 | return `github-repository_${this.githubRepository.ownerRepo}_cache`; 47 | } 48 | } 49 | 50 | interface RepoCache { 51 | cachedAt: number; 52 | data: RepoData | null; 53 | } 54 | -------------------------------------------------------------------------------- /src/colors.ts: -------------------------------------------------------------------------------- 1 | export class Colors { 2 | public static language(language: string): string { 3 | return (this.colors as any)[language] || '#ccc'; 4 | } 5 | 6 | private static readonly colors = { 7 | '1C Enterprise': '#814CCC', 8 | 'ABAP': '#E8274B', 9 | 'ActionScript': '#882B0F', 10 | 'Ada': '#02f88c', 11 | 'Agda': '#315665', 12 | 'AGS Script': '#B9D9FF', 13 | 'Alloy': '#64C800', 14 | 'AMPL': '#E6EFBB', 15 | 'ANTLR': '#9DC3FF', 16 | 'API Blueprint': '#2ACCA8', 17 | 'APL': '#5A8164', 18 | 'AppleScript': '#101F1F', 19 | 'Arc': '#aa2afe', 20 | 'Arduino': '#bd79d1', 21 | 'ASP': '#6a40fd', 22 | 'AspectJ': '#a957b0', 23 | 'Assembly': '#6E4C13', 24 | 'ATS': '#1ac620', 25 | 'AutoHotkey': '#6594b9', 26 | 'AutoIt': '#1C3552', 27 | 'Ballerina': '#FF5000', 28 | 'Batchfile': '#C1F12E', 29 | 'BlitzMax': '#cd6400', 30 | 'Boo': '#d4bec1', 31 | 'Brainfuck': '#2F2530', 32 | 'C': '#555555', 33 | 'C#': '#178600', 34 | 'C++': '#f34b7d', 35 | 'Ceylon': '#dfa535', 36 | 'Chapel': '#8dc63f', 37 | 'Cirru': '#ccccff', 38 | 'Clarion': '#db901e', 39 | 'Clean': '#3F85AF', 40 | 'Click': '#E4E6F3', 41 | 'Clojure': '#db5855', 42 | 'CoffeeScript': '#244776', 43 | 'ColdFusion': '#ed2cd6', 44 | 'Common Lisp': '#3fb68b', 45 | 'Component Pascal': '#B0CE4E', 46 | 'Crystal': '#776791', 47 | 'CSS': '#563d7c', 48 | 'Cuda': '#3A4E3A', 49 | 'D': '#ba595e', 50 | 'Dart': '#00B4AB', 51 | 'DataWeave': '#003a52', 52 | 'DM': '#447265', 53 | 'Dogescript': '#cca760', 54 | 'Dylan': '#6c616e', 55 | 'E': '#ccce35', 56 | 'eC': '#913960', 57 | 'ECL': '#8a1267', 58 | 'Eiffel': '#946d57', 59 | 'Elixir': '#6e4a7e', 60 | 'Elm': '#60B5CC', 61 | 'Emacs Lisp': '#c065db', 62 | 'EmberScript': '#FFF4F3', 63 | 'EQ': '#a78649', 64 | 'Erlang': '#B83998', 65 | 'F#': '#b845fc', 66 | 'Factor': '#636746', 67 | 'Fancy': '#7b9db4', 68 | 'Fantom': '#14253c', 69 | 'FLUX': '#88ccff', 70 | 'Forth': '#341708', 71 | 'Fortran': '#4d41b1', 72 | 'FreeMarker': '#0050b2', 73 | 'Frege': '#00cafe', 74 | 'Game Maker Language': '#8fb200', 75 | 'Genie': '#fb855d', 76 | 'Gherkin': '#5B2063', 77 | 'Glyph': '#e4cc98', 78 | 'Gnuplot': '#f0a9f0', 79 | 'Go': '#375eab', 80 | 'Golo': '#88562A', 81 | 'Gosu': '#82937f', 82 | 'Grammatical Framework': '#79aa7a', 83 | 'Groovy': '#e69f56', 84 | 'Hack': '#878787', 85 | 'Harbour': '#0e60e3', 86 | 'Haskell': '#5e5086', 87 | 'Haxe': '#df7900', 88 | 'HTML': '#e34c26', 89 | 'Hy': '#7790B2', 90 | 'IDL': '#a3522f', 91 | 'Io': '#a9188d', 92 | 'Ioke': '#078193', 93 | 'Isabelle': '#FEFE00', 94 | 'J': '#9EEDFF', 95 | 'Java': '#b07219', 96 | 'JavaScript': '#f1e05a', 97 | 'Jolie': '#843179', 98 | 'JSONiq': '#40d47e', 99 | 'Julia': '#a270ba', 100 | 'Jupyter Notebook': '#DA5B0B', 101 | 'Kotlin': '#F18E33', 102 | 'KRL': '#28431f', 103 | 'Lasso': '#999999', 104 | 'Lex': '#DBCA00', 105 | 'LiveScript': '#499886', 106 | 'LLVM': '#185619', 107 | 'LOLCODE': '#cc9900', 108 | 'LookML': '#652B81', 109 | 'LSL': '#3d9970', 110 | 'Lua': '#000080', 111 | 'Makefile': '#427819', 112 | 'Mask': '#f97732', 113 | 'Matlab': '#e16737', 114 | 'Max': '#c4a79c', 115 | 'MAXScript': '#00a6a6', 116 | 'Mercury': '#ff2b2b', 117 | 'Meson': '#007800', 118 | 'Metal': '#8f14e9', 119 | 'Mirah': '#c7a938', 120 | 'MQL4': '#62A8D6', 121 | 'MQL5': '#4A76B8', 122 | 'MTML': '#b7e1f4', 123 | 'NCL': '#28431f', 124 | 'Nearley': '#990000', 125 | 'Nemerle': '#3d3c6e', 126 | 'nesC': '#94B0C7', 127 | 'NetLinx': '#0aa0ff', 128 | 'NetLinx+ERB': '#747faa', 129 | 'NetLogo': '#ff6375', 130 | 'NewLisp': '#87AED7', 131 | 'Nim': '#37775b', 132 | 'Nit': '#009917', 133 | 'Nix': '#7e7eff', 134 | 'Nu': '#c9df40', 135 | 'Objective-C': '#438eff', 136 | 'Objective-C++': '#6866fb', 137 | 'Objective-J': '#ff0c5a', 138 | 'OCaml': '#3be133', 139 | 'Omgrofl': '#cabbff', 140 | 'ooc': '#b0b77e', 141 | 'Opal': '#f7ede0', 142 | 'Oxygene': '#cdd0e3', 143 | 'Oz': '#fab738', 144 | 'P4': '#7055b5', 145 | 'Pan': '#cc0000', 146 | 'Papyrus': '#6600cc', 147 | 'Parrot': '#f3ca0a', 148 | 'Pascal': '#E3F171', 149 | 'PAWN': '#dbb284', 150 | 'Pep8': '#C76F5B', 151 | 'Perl': '#0298c3', 152 | 'Perl 6': '#0000fb', 153 | 'PHP': '#4F5D95', 154 | 'PigLatin': '#fcd7de', 155 | 'Pike': '#005390', 156 | 'PLSQL': '#dad8d8', 157 | 'PogoScript': '#d80074', 158 | 'PostScript': '#da291c', 159 | 'PowerBuilder': '#8f0f8d', 160 | 'PowerShell': '#012456', 161 | 'Processing': '#0096D8', 162 | 'Prolog': '#74283c', 163 | 'Propeller Spin': '#7fa2a7', 164 | 'Puppet': '#302B6D', 165 | 'PureBasic': '#5a6986', 166 | 'PureScript': '#1D222D', 167 | 'Python': '#3572A5', 168 | 'QML': '#44a51c', 169 | 'R': '#198CE7', 170 | 'Racket': '#22228f', 171 | 'Ragel': '#9d5200', 172 | 'RAML': '#77d9fb', 173 | 'Rascal': '#fffaa0', 174 | 'Rebol': '#358a5b', 175 | 'Red': '#f50000', 176 | "Ren'Py": '#ff7f7f', 177 | 'Ring': '#0e60e3', 178 | 'Roff': '#ecdebe', 179 | 'Rouge': '#cc0088', 180 | 'Ruby': '#701516', 181 | 'RUNOFF': '#665a4e', 182 | 'Rust': '#dea584', 183 | 'SaltStack': '#646464', 184 | 'SAS': '#B34936', 185 | 'Scala': '#c22d40', 186 | 'Scheme': '#1e4aec', 187 | 'Self': '#0579aa', 188 | 'Shell': '#89e051', 189 | 'Shen': '#120F14', 190 | 'Slash': '#007eff', 191 | 'Smalltalk': '#596706', 192 | 'SourcePawn': '#5c7611', 193 | 'SQF': '#3F3F3F', 194 | 'Squirrel': '#800000', 195 | 'SRecode Template': '#348a34', 196 | 'Stan': '#b2011d', 197 | 'Standard ML': '#dc566d', 198 | 'SuperCollider': '#46390b', 199 | 'Swift': '#ffac45', 200 | 'SystemVerilog': '#DAE1C2', 201 | 'Tcl': '#e4cc98', 202 | 'Terra': '#00004c', 203 | 'TeX': '#3D6117', 204 | 'TI Program': '#A0AA87', 205 | 'Turing': '#cf142b', 206 | 'TypeScript': '#2b7489', 207 | 'UnrealScript': '#a54c4d', 208 | 'Vala': '#fbe5cd', 209 | 'Verilog': '#b2b7f8', 210 | 'VHDL': '#adb2cb', 211 | 'Vim script': '#199f4b', 212 | 'Visual Basic': '#945db7', 213 | 'Volt': '#1F1F1F', 214 | 'Vue': '#2c3e50', 215 | 'WebAssembly': '#04133b', 216 | 'wisp': '#7582D1', 217 | 'X10': '#4B6BEF', 218 | 'xBase': '#403a40', 219 | 'XC': '#99DA07', 220 | 'XQuery': '#5232e7', 221 | 'XSLT': '#EB8CEB', 222 | 'Yacc': '#4B6C4B', 223 | 'Zephir': '#118f9e' 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /src/github-repository.ts: -------------------------------------------------------------------------------- 1 | import { Seed, property, html, svg, TemplateResult } from '@nutmeg/seed'; 2 | import approximateNumber from 'approximate-number'; 3 | 4 | import { Cache } from './cache'; 5 | import { EmptyRepo, Repo, RepoData } from './repo'; 6 | 7 | export class GithubRepository extends Seed { 8 | @property({ type: String }) public ownerRepo: string = ''; 9 | 10 | private _repo: Repo | EmptyRepo = new EmptyRepo(); 11 | private cache: Cache; 12 | private error: string | undefined; 13 | private pending = false; 14 | 15 | constructor() { 16 | super(); 17 | this.cache = new Cache(this); 18 | } 19 | 20 | /** The component instance has been inserted into the DOM. */ 21 | public connectedCallback() { 22 | super.connectedCallback(); 23 | } 24 | 25 | /** The component instance has been removed from the DOM. */ 26 | public disconnectedCallback() { 27 | super.disconnectedCallback(); 28 | } 29 | 30 | /** Watch for changes to these attributes. */ 31 | public static get observedAttributes(): string[] { 32 | return super.observedAttributes; 33 | } 34 | 35 | /** Rerender when the observed attributes change. */ 36 | public attributeChangedCallback(name: string, oldValue: any, newValue: any) { 37 | super.attributeChangedCallback(name, oldValue, newValue); 38 | } 39 | 40 | private get repo(): Repo | EmptyRepo { 41 | if (this.cache.data && !!this.ownerRepo && this.ownerRepo !== this._repo.fullName) { 42 | this._repo = new Repo(this.cache.data); 43 | } 44 | if (!this._repo || this.cache.expired || this.ownerRepo !== this._repo.fullName) { 45 | this.fetchRepository(); 46 | } 47 | return this._repo; 48 | } 49 | 50 | private async fetchRepository(): Promise { 51 | if (this.pending || !this.ownerRepo) { 52 | return; 53 | } 54 | this.pending = true; 55 | const response = await fetch(`https://api.github.com/repos/${this.ownerRepo}`); 56 | const data = await response.json(); 57 | if (response.status === 200) { 58 | this._repo = new Repo(data); 59 | this.cache.data = data; 60 | } else { 61 | this.error = data.message; 62 | } 63 | this.pending = false; 64 | this.render(); 65 | } 66 | 67 | private countDisplay(count: number): string { 68 | return approximateNumber(count); 69 | } 70 | 71 | /** Styling for the component. */ 72 | public get styles(): TemplateResult { 73 | return html` 74 | 293 | `; 294 | } 295 | 296 | private get logo(): TemplateResult { 297 | return svg` 298 | 299 | 300 | 301 | 302 | `; 303 | } 304 | 305 | private get issuesIcon(): TemplateResult { 306 | return svg` 307 | 308 | 309 | 310 | `; 311 | } 312 | 313 | private get forkIcon(): TemplateResult { 314 | return svg` 315 | 316 | 317 | 318 | `; 319 | } 320 | 321 | private get starIcon(): TemplateResult { 322 | return html` 323 | 324 | 325 | 326 | `; 327 | } 328 | 329 | private get watchIcon(): TemplateResult { 330 | return html` 331 | 332 | 333 | 334 | `; 335 | } 336 | 337 | private get headerTemplate(): TemplateResult { 338 | return html` 339 | 346 | `; 347 | } 348 | 349 | private get descriptionTemplate(): TemplateResult { 350 | return html` 351 |
352 | 353 | ${this.repo.description} 354 | ${this.repo.homepage ? this.homepageTempate : ''} 355 | 356 |
357 | `; 358 | } 359 | 360 | private get homepageTempate(): TemplateResult { 361 | return html` 362 | 363 | ${this.repo.displayHomepage} 364 | 365 | `; 366 | } 367 | 368 | private get cloneTemplate(): TemplateResult { 369 | return html` 370 |
371 |
${this.repo.sshUrl}
372 |
373 | `; 374 | } 375 | 376 | private get countsTemplate(): TemplateResult { 377 | return html` 378 |
379 | 380 | ${this.watchIcon} Watchers 381 | ${this.countDisplay(this.repo.watchersCount)} 382 | 383 | 384 | ${this.starIcon} Stars 385 | ${this.countDisplay(this.repo.starsCount)} 386 | 387 | 388 | ${this.forkIcon} Forks 389 | ${this.countDisplay(this.repo.forksCount)} 390 | 391 | 392 | ${this.issuesIcon} Issues 393 | ${this.countDisplay(this.repo.openIssuesCount)} 394 | 395 |
396 | `; 397 | } 398 | 399 | private get languageTemplate(): TemplateResult { 400 | return html` 401 |
402 | 403 | ${this.repo.language} 404 |
405 | `; 406 | } 407 | 408 | private get footerTemplate(): TemplateResult { 409 | return html` 410 | 415 | `; 416 | } 417 | 418 | private get errorTemplate(): TemplateResult { 419 | return html` 420 |
421 |
Error getting ${this.ownerRepo} details from from GitHub:
422 |
"${this.error}"
423 |
424 | `; 425 | } 426 | 427 | private get loadingTemplate(): TemplateResult { 428 | return html` 429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 | `; 444 | } 445 | 446 | private get contentTemplate(): TemplateResult { 447 | return html` 448 |
449 | 450 | ${this.headerTemplate} 451 | ${this.descriptionTemplate} 452 | ${this.countsTemplate} 453 | ${this.cloneTemplate} 454 |
455 | 456 |
457 | ${this.footerTemplate} 458 |
459 | `; 460 | } 461 | 462 | /** HTML Template for the component. */ 463 | public get template(): TemplateResult { 464 | if (this.error) { 465 | return this.errorTemplate; 466 | } else if (!!this.repo.fullName) { 467 | return this.contentTemplate; 468 | } else { 469 | return this.loadingTemplate; 470 | } 471 | } 472 | } 473 | 474 | window.customElements.define('github-repository', GithubRepository); 475 | -------------------------------------------------------------------------------- /src/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'approximate-number' { 2 | function approximateNumber(num: number, opts?: {}): string; 3 | export default approximateNumber; 4 | } 5 | -------------------------------------------------------------------------------- /src/license.ts: -------------------------------------------------------------------------------- 1 | export class License { 2 | private data: LicenseData; 3 | 4 | constructor(license: LicenseData) { 5 | this.data = license; 6 | } 7 | 8 | public get name(): string { 9 | return this.data ? this.data.name : 'Unknown license'; 10 | } 11 | } 12 | 13 | export interface LicenseData { 14 | key: string; 15 | name: string; 16 | spdx_id: string; 17 | url: string; 18 | } 19 | -------------------------------------------------------------------------------- /src/repo.ts: -------------------------------------------------------------------------------- 1 | import { Colors } from './colors'; 2 | import { User, UserData } from './user'; 3 | import { License, LicenseData } from './license'; 4 | 5 | export class Repo { 6 | public owner: User; 7 | private readonly months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; 8 | private license: License; 9 | 10 | constructor(private data: RepoData) { 11 | this.license = new License(data.license); 12 | this.owner = new User(data.owner); 13 | } 14 | 15 | public get name(): string { 16 | return this.data.name; 17 | } 18 | 19 | public get fullName(): string { 20 | return this.data.full_name; 21 | } 22 | 23 | public get htmlUrl(): string { 24 | return this.data.html_url; 25 | } 26 | 27 | public get description(): string { 28 | return this.data.description; 29 | } 30 | 31 | public get sshUrl(): string { 32 | return this.data.ssh_url; 33 | } 34 | 35 | public get displayPushedAt(): string { 36 | const month = this.months[this.pushedAt.getMonth()]; 37 | return `${month} ${this.pushedAt.getDate()} ${this.pushedYear}`; 38 | } 39 | 40 | public get homepage(): string { 41 | return this.data.homepage; 42 | } 43 | 44 | public get displayHomepage(): string { 45 | return (this.homepage || '') 46 | .replace('http://www.', '') 47 | .replace('https://www.', '') 48 | .replace('http://', '') 49 | .replace('https://', ''); 50 | } 51 | 52 | public get starsCount(): number { 53 | return this.data.stargazers_count; 54 | } 55 | 56 | public get watchersCount(): number { 57 | return this.data.subscribers_count; 58 | } 59 | 60 | public get openIssuesCount(): number { 61 | return this.data.open_issues_count; 62 | } 63 | 64 | public get forksCount(): number { 65 | return this.data.forks_count; 66 | } 67 | 68 | public get language(): string { 69 | return this.data.language || 'Unknown language'; 70 | } 71 | 72 | public get languageColor(): string { 73 | return Colors.language(this.data.language); 74 | } 75 | 76 | public get displayLicense(): string { 77 | return this.license.name; 78 | } 79 | 80 | private get pushedAt(): Date { 81 | return new Date(Date.parse(this.data.pushed_at)); 82 | } 83 | 84 | private get pushedYear(): string { 85 | return (new Date()).getFullYear() === this.pushedAt.getFullYear() ? '' : `${this.pushedAt.getFullYear()}`; 86 | } 87 | } 88 | 89 | export class EmptyRepo { 90 | public id = 0; 91 | public fullName = ''; 92 | public owner = { htmlUrl: '', login: '' }; 93 | public htmlUrl = ''; 94 | public name = ''; 95 | public description = ''; 96 | public homepage = ''; 97 | public displayHomepage = ''; 98 | public sshUrl = ''; 99 | public watchersCount = 0; 100 | public starsCount = 0; 101 | public openIssuesCount = 0; 102 | public forksCount = 0; 103 | public language = ''; 104 | public languageColor = ''; 105 | public displayPushedAt = ''; 106 | public displayLicense = ''; 107 | }; 108 | 109 | export interface RepoData { 110 | id: number; 111 | name: string; 112 | full_name: string; 113 | owner: UserData; 114 | private: boolean; 115 | html_url: string; 116 | description: string; 117 | fork: boolean; 118 | url: string; 119 | forks_url: string; 120 | keys_url: string; 121 | collaborators_url: string; 122 | teams_url: string; 123 | hooks_url: string; 124 | issue_events_url: string; 125 | events_url: string; 126 | assignees_url: string; 127 | branches_url: string; 128 | tags_url: string; 129 | blobs_url: string; 130 | git_tags_url: string; 131 | git_refs_url: string; 132 | trees_url: string; 133 | statuses_url: string; 134 | languages_url: string; 135 | stargazers_url: string; 136 | contributors_url: string; 137 | subscribers_url: string; 138 | subscription_url: string; 139 | commits_url: string; 140 | git_commits_url: string; 141 | comments_url: string; 142 | issue_comment_url: string; 143 | contents_url: string; 144 | compare_url: string; 145 | merges_url: string; 146 | archive_url: string; 147 | downloads_url: string; 148 | issues_url: string; 149 | pulls_url: string; 150 | milestones_url: string; 151 | notifications_url: string; 152 | labels_url: string; 153 | releases_url: string; 154 | deployments_url: string; 155 | created_at: string; 156 | updated_at: string; 157 | pushed_at: string; 158 | git_url: string; 159 | ssh_url: string; 160 | clone_url: string; 161 | svn_url: string; 162 | homepage: string; 163 | size: number; 164 | stargazers_count: number; 165 | watchers_count: number; 166 | language: string; 167 | has_issues: boolean; 168 | has_projects: boolean; 169 | has_downloads: boolean; 170 | has_wiki: boolean; 171 | has_pages: boolean; 172 | forks_count: number; 173 | mirror_url: string | null; 174 | archived: boolean; 175 | open_issues_count: number; 176 | license: LicenseData; 177 | forks: number; 178 | open_issues: number; 179 | watchers: number; 180 | default_branch: string; 181 | network_count: number; 182 | subscribers_count: number; 183 | } 184 | -------------------------------------------------------------------------------- /src/user.ts: -------------------------------------------------------------------------------- 1 | export class User { 2 | private data: UserData; 3 | 4 | constructor(user: UserData) { 5 | this.data = user; 6 | } 7 | 8 | public get htmlUrl(): string { 9 | return this.data.html_url; 10 | } 11 | 12 | public get login(): string { 13 | return this.data.login; 14 | } 15 | } 16 | 17 | export interface UserData { 18 | login: string; 19 | id: number; 20 | avatar_url: string; 21 | gravatar_id: string; 22 | url: string; 23 | html_url: string; 24 | followers_url: string; 25 | following_url: string; 26 | gists_url: string; 27 | starred_url: string; 28 | subscriptions_url: string; 29 | organizations_url: string; 30 | repos_url: string; 31 | events_url: string; 32 | received_events_url: string; 33 | type: 'User' 34 | site_admin: boolean; 35 | } 36 | -------------------------------------------------------------------------------- /test/data/abraham/empty.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 119274896, 3 | "name": "empty", 4 | "full_name": "abraham/empty", 5 | "owner": { 6 | "login": "abraham", 7 | "id": 3341, 8 | "avatar_url": "https://avatars1.githubusercontent.com/u/3341?v=4", 9 | "gravatar_id": "", 10 | "url": "https://api.github.com/users/abraham", 11 | "html_url": "https://github.com/abraham", 12 | "followers_url": "https://api.github.com/users/abraham/followers", 13 | "following_url": "https://api.github.com/users/abraham/following{/other_user}", 14 | "gists_url": "https://api.github.com/users/abraham/gists{/gist_id}", 15 | "starred_url": "https://api.github.com/users/abraham/starred{/owner}{/repo}", 16 | "subscriptions_url": "https://api.github.com/users/abraham/subscriptions", 17 | "organizations_url": "https://api.github.com/users/abraham/orgs", 18 | "repos_url": "https://api.github.com/users/abraham/repos", 19 | "events_url": "https://api.github.com/users/abraham/events{/privacy}", 20 | "received_events_url": "https://api.github.com/users/abraham/received_events", 21 | "type": "User", 22 | "site_admin": false 23 | }, 24 | "private": false, 25 | "html_url": "https://github.com/abraham/empty", 26 | "description": null, 27 | "fork": false, 28 | "url": "https://api.github.com/repos/abraham/empty", 29 | "forks_url": "https://api.github.com/repos/abraham/empty/forks", 30 | "keys_url": "https://api.github.com/repos/abraham/empty/keys{/key_id}", 31 | "collaborators_url": "https://api.github.com/repos/abraham/empty/collaborators{/collaborator}", 32 | "teams_url": "https://api.github.com/repos/abraham/empty/teams", 33 | "hooks_url": "https://api.github.com/repos/abraham/empty/hooks", 34 | "issue_events_url": "https://api.github.com/repos/abraham/empty/issues/events{/number}", 35 | "events_url": "https://api.github.com/repos/abraham/empty/events", 36 | "assignees_url": "https://api.github.com/repos/abraham/empty/assignees{/user}", 37 | "branches_url": "https://api.github.com/repos/abraham/empty/branches{/branch}", 38 | "tags_url": "https://api.github.com/repos/abraham/empty/tags", 39 | "blobs_url": "https://api.github.com/repos/abraham/empty/git/blobs{/sha}", 40 | "git_tags_url": "https://api.github.com/repos/abraham/empty/git/tags{/sha}", 41 | "git_refs_url": "https://api.github.com/repos/abraham/empty/git/refs{/sha}", 42 | "trees_url": "https://api.github.com/repos/abraham/empty/git/trees{/sha}", 43 | "statuses_url": "https://api.github.com/repos/abraham/empty/statuses/{sha}", 44 | "languages_url": "https://api.github.com/repos/abraham/empty/languages", 45 | "stargazers_url": "https://api.github.com/repos/abraham/empty/stargazers", 46 | "contributors_url": "https://api.github.com/repos/abraham/empty/contributors", 47 | "subscribers_url": "https://api.github.com/repos/abraham/empty/subscribers", 48 | "subscription_url": "https://api.github.com/repos/abraham/empty/subscription", 49 | "commits_url": "https://api.github.com/repos/abraham/empty/commits{/sha}", 50 | "git_commits_url": "https://api.github.com/repos/abraham/empty/git/commits{/sha}", 51 | "comments_url": "https://api.github.com/repos/abraham/empty/comments{/number}", 52 | "issue_comment_url": "https://api.github.com/repos/abraham/empty/issues/comments{/number}", 53 | "contents_url": "https://api.github.com/repos/abraham/empty/contents/{+path}", 54 | "compare_url": "https://api.github.com/repos/abraham/empty/compare/{base}...{head}", 55 | "merges_url": "https://api.github.com/repos/abraham/empty/merges", 56 | "archive_url": "https://api.github.com/repos/abraham/empty/{archive_format}{/ref}", 57 | "downloads_url": "https://api.github.com/repos/abraham/empty/downloads", 58 | "issues_url": "https://api.github.com/repos/abraham/empty/issues{/number}", 59 | "pulls_url": "https://api.github.com/repos/abraham/empty/pulls{/number}", 60 | "milestones_url": "https://api.github.com/repos/abraham/empty/milestones{/number}", 61 | "notifications_url": "https://api.github.com/repos/abraham/empty/notifications{?since,all,participating}", 62 | "labels_url": "https://api.github.com/repos/abraham/empty/labels{/name}", 63 | "releases_url": "https://api.github.com/repos/abraham/empty/releases{/id}", 64 | "deployments_url": "https://api.github.com/repos/abraham/empty/deployments", 65 | "created_at": "2017-01-28T16:17:12Z", 66 | "updated_at": "2017-01-28T16:17:12Z", 67 | "pushed_at": "2017-01-28T16:17:13Z", 68 | "git_url": "git://github.com/abraham/empty.git", 69 | "ssh_url": "git@github.com:abraham/empty.git", 70 | "clone_url": "https://github.com/abraham/empty.git", 71 | "svn_url": "https://github.com/abraham/empty", 72 | "homepage": null, 73 | "size": 0, 74 | "stargazers_count": 0, 75 | "watchers_count": 0, 76 | "language": null, 77 | "has_issues": true, 78 | "has_projects": true, 79 | "has_downloads": true, 80 | "has_wiki": true, 81 | "has_pages": false, 82 | "forks_count": 0, 83 | "mirror_url": null, 84 | "archived": false, 85 | "open_issues_count": 0, 86 | "license": null, 87 | "forks": 0, 88 | "open_issues": 0, 89 | "watchers": 0, 90 | "default_branch": "master", 91 | "network_count": 0, 92 | "subscribers_count": 1 93 | } 94 | -------------------------------------------------------------------------------- /test/data/abraham/twitter-status.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 117631949, 3 | "name": "twitter-status", 4 | "full_name": "abraham/twitter-status", 5 | "owner": { 6 | "login": "abraham", 7 | "id": 3341, 8 | "avatar_url": "https://avatars1.githubusercontent.com/u/3341?v=4", 9 | "gravatar_id": "", 10 | "url": "https://api.github.com/users/abraham", 11 | "html_url": "https://github.com/abraham", 12 | "followers_url": "https://api.github.com/users/abraham/followers", 13 | "following_url": "https://api.github.com/users/abraham/following{/other_user}", 14 | "gists_url": "https://api.github.com/users/abraham/gists{/gist_id}", 15 | "starred_url": "https://api.github.com/users/abraham/starred{/owner}{/repo}", 16 | "subscriptions_url": "https://api.github.com/users/abraham/subscriptions", 17 | "organizations_url": "https://api.github.com/users/abraham/orgs", 18 | "repos_url": "https://api.github.com/users/abraham/repos", 19 | "events_url": "https://api.github.com/users/abraham/events{/privacy}", 20 | "received_events_url": "https://api.github.com/users/abraham/received_events", 21 | "type": "User", 22 | "site_admin": false 23 | }, 24 | "private": false, 25 | "html_url": "https://github.com/abraham/twitter-status", 26 | "description": "Twitter Status Web Component", 27 | "fork": false, 28 | "url": "https://api.github.com/repos/abraham/twitter-status", 29 | "forks_url": "https://api.github.com/repos/abraham/twitter-status/forks", 30 | "keys_url": "https://api.github.com/repos/abraham/twitter-status/keys{/key_id}", 31 | "collaborators_url": "https://api.github.com/repos/abraham/twitter-status/collaborators{/collaborator}", 32 | "teams_url": "https://api.github.com/repos/abraham/twitter-status/teams", 33 | "hooks_url": "https://api.github.com/repos/abraham/twitter-status/hooks", 34 | "issue_events_url": "https://api.github.com/repos/abraham/twitter-status/issues/events{/number}", 35 | "events_url": "https://api.github.com/repos/abraham/twitter-status/events", 36 | "assignees_url": "https://api.github.com/repos/abraham/twitter-status/assignees{/user}", 37 | "branches_url": "https://api.github.com/repos/abraham/twitter-status/branches{/branch}", 38 | "tags_url": "https://api.github.com/repos/abraham/twitter-status/tags", 39 | "blobs_url": "https://api.github.com/repos/abraham/twitter-status/git/blobs{/sha}", 40 | "git_tags_url": "https://api.github.com/repos/abraham/twitter-status/git/tags{/sha}", 41 | "git_refs_url": "https://api.github.com/repos/abraham/twitter-status/git/refs{/sha}", 42 | "trees_url": "https://api.github.com/repos/abraham/twitter-status/git/trees{/sha}", 43 | "statuses_url": "https://api.github.com/repos/abraham/twitter-status/statuses/{sha}", 44 | "languages_url": "https://api.github.com/repos/abraham/twitter-status/languages", 45 | "stargazers_url": "https://api.github.com/repos/abraham/twitter-status/stargazers", 46 | "contributors_url": "https://api.github.com/repos/abraham/twitter-status/contributors", 47 | "subscribers_url": "https://api.github.com/repos/abraham/twitter-status/subscribers", 48 | "subscription_url": "https://api.github.com/repos/abraham/twitter-status/subscription", 49 | "commits_url": "https://api.github.com/repos/abraham/twitter-status/commits{/sha}", 50 | "git_commits_url": "https://api.github.com/repos/abraham/twitter-status/git/commits{/sha}", 51 | "comments_url": "https://api.github.com/repos/abraham/twitter-status/comments{/number}", 52 | "issue_comment_url": "https://api.github.com/repos/abraham/twitter-status/issues/comments{/number}", 53 | "contents_url": "https://api.github.com/repos/abraham/twitter-status/contents/{+path}", 54 | "compare_url": "https://api.github.com/repos/abraham/twitter-status/compare/{base}...{head}", 55 | "merges_url": "https://api.github.com/repos/abraham/twitter-status/merges", 56 | "archive_url": "https://api.github.com/repos/abraham/twitter-status/{archive_format}{/ref}", 57 | "downloads_url": "https://api.github.com/repos/abraham/twitter-status/downloads", 58 | "issues_url": "https://api.github.com/repos/abraham/twitter-status/issues{/number}", 59 | "pulls_url": "https://api.github.com/repos/abraham/twitter-status/pulls{/number}", 60 | "milestones_url": "https://api.github.com/repos/abraham/twitter-status/milestones{/number}", 61 | "notifications_url": "https://api.github.com/repos/abraham/twitter-status/notifications{?since,all,participating}", 62 | "labels_url": "https://api.github.com/repos/abraham/twitter-status/labels{/name}", 63 | "releases_url": "https://api.github.com/repos/abraham/twitter-status/releases{/id}", 64 | "deployments_url": "https://api.github.com/repos/abraham/twitter-status/deployments", 65 | "created_at": "2018-01-16T04:39:27Z", 66 | "updated_at": "2018-01-27T16:36:54Z", 67 | "pushed_at": "2018-01-27T23:55:32Z", 68 | "git_url": "git://github.com/abraham/twitter-status.git", 69 | "ssh_url": "git@github.com:abraham/twitter-status.git", 70 | "clone_url": "https://github.com/abraham/twitter-status.git", 71 | "svn_url": "https://github.com/abraham/twitter-status", 72 | "homepage": "https://www.npmjs.com/package/twitter-status", 73 | "size": 1418, 74 | "stargazers_count": 5, 75 | "watchers_count": 5, 76 | "language": "TypeScript", 77 | "has_issues": true, 78 | "has_projects": true, 79 | "has_downloads": true, 80 | "has_wiki": false, 81 | "has_pages": false, 82 | "forks_count": 1, 83 | "mirror_url": null, 84 | "archived": false, 85 | "open_issues_count": 6, 86 | "license": { 87 | "key": "mit", 88 | "name": "MIT License", 89 | "spdx_id": "MIT", 90 | "url": "https://api.github.com/licenses/mit" 91 | }, 92 | "forks": 1, 93 | "open_issues": 6, 94 | "watchers": 5, 95 | "default_branch": "master", 96 | "network_count": 1, 97 | "subscribers_count": 1 98 | } 99 | -------------------------------------------------------------------------------- /test/data/abraham/twitteroauth.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 128818, 3 | "name": "twitteroauth", 4 | "full_name": "abraham/twitteroauth", 5 | "owner": { 6 | "login": "abraham", 7 | "id": 3341, 8 | "avatar_url": "https://avatars1.githubusercontent.com/u/3341?v=4", 9 | "gravatar_id": "", 10 | "url": "https://api.github.com/users/abraham", 11 | "html_url": "https://github.com/abraham", 12 | "followers_url": "https://api.github.com/users/abraham/followers", 13 | "following_url": "https://api.github.com/users/abraham/following{/other_user}", 14 | "gists_url": "https://api.github.com/users/abraham/gists{/gist_id}", 15 | "starred_url": "https://api.github.com/users/abraham/starred{/owner}{/repo}", 16 | "subscriptions_url": "https://api.github.com/users/abraham/subscriptions", 17 | "organizations_url": "https://api.github.com/users/abraham/orgs", 18 | "repos_url": "https://api.github.com/users/abraham/repos", 19 | "events_url": "https://api.github.com/users/abraham/events{/privacy}", 20 | "received_events_url": "https://api.github.com/users/abraham/received_events", 21 | "type": "User", 22 | "site_admin": false 23 | }, 24 | "private": false, 25 | "html_url": "https://github.com/abraham/twitteroauth", 26 | "description": "The most popular PHP library for use with the Twitter OAuth REST API.", 27 | "fork": false, 28 | "url": "https://api.github.com/repos/abraham/twitteroauth", 29 | "forks_url": "https://api.github.com/repos/abraham/twitteroauth/forks", 30 | "keys_url": "https://api.github.com/repos/abraham/twitteroauth/keys{/key_id}", 31 | "collaborators_url": "https://api.github.com/repos/abraham/twitteroauth/collaborators{/collaborator}", 32 | "teams_url": "https://api.github.com/repos/abraham/twitteroauth/teams", 33 | "hooks_url": "https://api.github.com/repos/abraham/twitteroauth/hooks", 34 | "issue_events_url": "https://api.github.com/repos/abraham/twitteroauth/issues/events{/number}", 35 | "events_url": "https://api.github.com/repos/abraham/twitteroauth/events", 36 | "assignees_url": "https://api.github.com/repos/abraham/twitteroauth/assignees{/user}", 37 | "branches_url": "https://api.github.com/repos/abraham/twitteroauth/branches{/branch}", 38 | "tags_url": "https://api.github.com/repos/abraham/twitteroauth/tags", 39 | "blobs_url": "https://api.github.com/repos/abraham/twitteroauth/git/blobs{/sha}", 40 | "git_tags_url": "https://api.github.com/repos/abraham/twitteroauth/git/tags{/sha}", 41 | "git_refs_url": "https://api.github.com/repos/abraham/twitteroauth/git/refs{/sha}", 42 | "trees_url": "https://api.github.com/repos/abraham/twitteroauth/git/trees{/sha}", 43 | "statuses_url": "https://api.github.com/repos/abraham/twitteroauth/statuses/{sha}", 44 | "languages_url": "https://api.github.com/repos/abraham/twitteroauth/languages", 45 | "stargazers_url": "https://api.github.com/repos/abraham/twitteroauth/stargazers", 46 | "contributors_url": "https://api.github.com/repos/abraham/twitteroauth/contributors", 47 | "subscribers_url": "https://api.github.com/repos/abraham/twitteroauth/subscribers", 48 | "subscription_url": "https://api.github.com/repos/abraham/twitteroauth/subscription", 49 | "commits_url": "https://api.github.com/repos/abraham/twitteroauth/commits{/sha}", 50 | "git_commits_url": "https://api.github.com/repos/abraham/twitteroauth/git/commits{/sha}", 51 | "comments_url": "https://api.github.com/repos/abraham/twitteroauth/comments{/number}", 52 | "issue_comment_url": "https://api.github.com/repos/abraham/twitteroauth/issues/comments{/number}", 53 | "contents_url": "https://api.github.com/repos/abraham/twitteroauth/contents/{+path}", 54 | "compare_url": "https://api.github.com/repos/abraham/twitteroauth/compare/{base}...{head}", 55 | "merges_url": "https://api.github.com/repos/abraham/twitteroauth/merges", 56 | "archive_url": "https://api.github.com/repos/abraham/twitteroauth/{archive_format}{/ref}", 57 | "downloads_url": "https://api.github.com/repos/abraham/twitteroauth/downloads", 58 | "issues_url": "https://api.github.com/repos/abraham/twitteroauth/issues{/number}", 59 | "pulls_url": "https://api.github.com/repos/abraham/twitteroauth/pulls{/number}", 60 | "milestones_url": "https://api.github.com/repos/abraham/twitteroauth/milestones{/number}", 61 | "notifications_url": "https://api.github.com/repos/abraham/twitteroauth/notifications{?since,all,participating}", 62 | "labels_url": "https://api.github.com/repos/abraham/twitteroauth/labels{/name}", 63 | "releases_url": "https://api.github.com/repos/abraham/twitteroauth/releases{/id}", 64 | "deployments_url": "https://api.github.com/repos/abraham/twitteroauth/deployments", 65 | "created_at": "2009-02-14T12:27:24Z", 66 | "updated_at": "2018-01-28T15:59:13Z", 67 | "pushed_at": "2018-01-09T02:40:48Z", 68 | "git_url": "git://github.com/abraham/twitteroauth.git", 69 | "ssh_url": "git@github.com:abraham/twitteroauth.git", 70 | "clone_url": "https://github.com/abraham/twitteroauth.git", 71 | "svn_url": "https://github.com/abraham/twitteroauth", 72 | "homepage": "https://twitteroauth.com", 73 | "size": 1233, 74 | "stargazers_count": 3551, 75 | "watchers_count": 3551, 76 | "language": "PHP", 77 | "has_issues": true, 78 | "has_projects": true, 79 | "has_downloads": true, 80 | "has_wiki": false, 81 | "has_pages": false, 82 | "forks_count": 1577, 83 | "mirror_url": null, 84 | "archived": false, 85 | "open_issues_count": 56, 86 | "license": { 87 | "key": "mit", 88 | "name": "MIT License", 89 | "spdx_id": "MIT", 90 | "url": "https://api.github.com/licenses/mit" 91 | }, 92 | "forks": 1577, 93 | "open_issues": 56, 94 | "watchers": 3551, 95 | "default_branch": "master", 96 | "network_count": 1577, 97 | "subscribers_count": 221 98 | } 99 | -------------------------------------------------------------------------------- /test/data/angular/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 24195339, 3 | "name": "angular", 4 | "full_name": "angular/angular", 5 | "owner": { 6 | "login": "angular", 7 | "id": 139426, 8 | "avatar_url": "https://avatars3.githubusercontent.com/u/139426?v=4", 9 | "gravatar_id": "", 10 | "url": "https://api.github.com/users/angular", 11 | "html_url": "https://github.com/angular", 12 | "followers_url": "https://api.github.com/users/angular/followers", 13 | "following_url": "https://api.github.com/users/angular/following{/other_user}", 14 | "gists_url": "https://api.github.com/users/angular/gists{/gist_id}", 15 | "starred_url": "https://api.github.com/users/angular/starred{/owner}{/repo}", 16 | "subscriptions_url": "https://api.github.com/users/angular/subscriptions", 17 | "organizations_url": "https://api.github.com/users/angular/orgs", 18 | "repos_url": "https://api.github.com/users/angular/repos", 19 | "events_url": "https://api.github.com/users/angular/events{/privacy}", 20 | "received_events_url": "https://api.github.com/users/angular/received_events", 21 | "type": "Organization", 22 | "site_admin": false 23 | }, 24 | "private": false, 25 | "html_url": "https://github.com/angular/angular", 26 | "description": "One framework. Mobile & desktop.", 27 | "fork": false, 28 | "url": "https://api.github.com/repos/angular/angular", 29 | "forks_url": "https://api.github.com/repos/angular/angular/forks", 30 | "keys_url": "https://api.github.com/repos/angular/angular/keys{/key_id}", 31 | "collaborators_url": "https://api.github.com/repos/angular/angular/collaborators{/collaborator}", 32 | "teams_url": "https://api.github.com/repos/angular/angular/teams", 33 | "hooks_url": "https://api.github.com/repos/angular/angular/hooks", 34 | "issue_events_url": "https://api.github.com/repos/angular/angular/issues/events{/number}", 35 | "events_url": "https://api.github.com/repos/angular/angular/events", 36 | "assignees_url": "https://api.github.com/repos/angular/angular/assignees{/user}", 37 | "branches_url": "https://api.github.com/repos/angular/angular/branches{/branch}", 38 | "tags_url": "https://api.github.com/repos/angular/angular/tags", 39 | "blobs_url": "https://api.github.com/repos/angular/angular/git/blobs{/sha}", 40 | "git_tags_url": "https://api.github.com/repos/angular/angular/git/tags{/sha}", 41 | "git_refs_url": "https://api.github.com/repos/angular/angular/git/refs{/sha}", 42 | "trees_url": "https://api.github.com/repos/angular/angular/git/trees{/sha}", 43 | "statuses_url": "https://api.github.com/repos/angular/angular/statuses/{sha}", 44 | "languages_url": "https://api.github.com/repos/angular/angular/languages", 45 | "stargazers_url": "https://api.github.com/repos/angular/angular/stargazers", 46 | "contributors_url": "https://api.github.com/repos/angular/angular/contributors", 47 | "subscribers_url": "https://api.github.com/repos/angular/angular/subscribers", 48 | "subscription_url": "https://api.github.com/repos/angular/angular/subscription", 49 | "commits_url": "https://api.github.com/repos/angular/angular/commits{/sha}", 50 | "git_commits_url": "https://api.github.com/repos/angular/angular/git/commits{/sha}", 51 | "comments_url": "https://api.github.com/repos/angular/angular/comments{/number}", 52 | "issue_comment_url": "https://api.github.com/repos/angular/angular/issues/comments{/number}", 53 | "contents_url": "https://api.github.com/repos/angular/angular/contents/{+path}", 54 | "compare_url": "https://api.github.com/repos/angular/angular/compare/{base}...{head}", 55 | "merges_url": "https://api.github.com/repos/angular/angular/merges", 56 | "archive_url": "https://api.github.com/repos/angular/angular/{archive_format}{/ref}", 57 | "downloads_url": "https://api.github.com/repos/angular/angular/downloads", 58 | "issues_url": "https://api.github.com/repos/angular/angular/issues{/number}", 59 | "pulls_url": "https://api.github.com/repos/angular/angular/pulls{/number}", 60 | "milestones_url": "https://api.github.com/repos/angular/angular/milestones{/number}", 61 | "notifications_url": "https://api.github.com/repos/angular/angular/notifications{?since,all,participating}", 62 | "labels_url": "https://api.github.com/repos/angular/angular/labels{/name}", 63 | "releases_url": "https://api.github.com/repos/angular/angular/releases{/id}", 64 | "deployments_url": "https://api.github.com/repos/angular/angular/deployments", 65 | "created_at": "2014-09-18T16:12:01Z", 66 | "updated_at": "2018-01-28T20:25:49Z", 67 | "pushed_at": "2018-01-28T16:48:17Z", 68 | "git_url": "git://github.com/angular/angular.git", 69 | "ssh_url": "git@github.com:angular/angular.git", 70 | "clone_url": "https://github.com/angular/angular.git", 71 | "svn_url": "https://github.com/angular/angular", 72 | "homepage": "https://angular.io", 73 | "size": 72588, 74 | "stargazers_count": 32460, 75 | "watchers_count": 32460, 76 | "language": "TypeScript", 77 | "has_issues": true, 78 | "has_projects": true, 79 | "has_downloads": true, 80 | "has_wiki": false, 81 | "has_pages": false, 82 | "forks_count": 8018, 83 | "mirror_url": null, 84 | "archived": false, 85 | "open_issues_count": 2097, 86 | "license": { 87 | "key": "mit", 88 | "name": "MIT License", 89 | "spdx_id": "MIT", 90 | "url": "https://api.github.com/licenses/mit" 91 | }, 92 | "forks": 8018, 93 | "open_issues": 2097, 94 | "watchers": 32460, 95 | "default_branch": "master", 96 | "organization": { 97 | "login": "angular", 98 | "id": 139426, 99 | "avatar_url": "https://avatars3.githubusercontent.com/u/139426?v=4", 100 | "gravatar_id": "", 101 | "url": "https://api.github.com/users/angular", 102 | "html_url": "https://github.com/angular", 103 | "followers_url": "https://api.github.com/users/angular/followers", 104 | "following_url": "https://api.github.com/users/angular/following{/other_user}", 105 | "gists_url": "https://api.github.com/users/angular/gists{/gist_id}", 106 | "starred_url": "https://api.github.com/users/angular/starred{/owner}{/repo}", 107 | "subscriptions_url": "https://api.github.com/users/angular/subscriptions", 108 | "organizations_url": "https://api.github.com/users/angular/orgs", 109 | "repos_url": "https://api.github.com/users/angular/repos", 110 | "events_url": "https://api.github.com/users/angular/events{/privacy}", 111 | "received_events_url": "https://api.github.com/users/angular/received_events", 112 | "type": "Organization", 113 | "site_admin": false 114 | }, 115 | "network_count": 8018, 116 | "subscribers_count": 2851 117 | } 118 | -------------------------------------------------------------------------------- /test/data/error.json: -------------------------------------------------------------------------------- 1 | { 2 | "message": "API rate limit exceeded for 71.90.116.85. (But here's the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)", 3 | "documentation_url": "https://developer.github.com/v3/#rate-limiting" 4 | } 5 | -------------------------------------------------------------------------------- /test/data/vuejs/vue.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 11730342, 3 | "name": "vue", 4 | "full_name": "vuejs/vue", 5 | "owner": { 6 | "login": "vuejs", 7 | "id": 6128107, 8 | "avatar_url": "https://avatars1.githubusercontent.com/u/6128107?v=4", 9 | "gravatar_id": "", 10 | "url": "https://api.github.com/users/vuejs", 11 | "html_url": "https://github.com/vuejs", 12 | "followers_url": "https://api.github.com/users/vuejs/followers", 13 | "following_url": "https://api.github.com/users/vuejs/following{/other_user}", 14 | "gists_url": "https://api.github.com/users/vuejs/gists{/gist_id}", 15 | "starred_url": "https://api.github.com/users/vuejs/starred{/owner}{/repo}", 16 | "subscriptions_url": "https://api.github.com/users/vuejs/subscriptions", 17 | "organizations_url": "https://api.github.com/users/vuejs/orgs", 18 | "repos_url": "https://api.github.com/users/vuejs/repos", 19 | "events_url": "https://api.github.com/users/vuejs/events{/privacy}", 20 | "received_events_url": "https://api.github.com/users/vuejs/received_events", 21 | "type": "Organization", 22 | "site_admin": false 23 | }, 24 | "private": false, 25 | "html_url": "https://github.com/vuejs/vue", 26 | "description": "🖖 A progressive, incrementally-adoptable JavaScript framework for building UI on the web.", 27 | "fork": false, 28 | "url": "https://api.github.com/repos/vuejs/vue", 29 | "forks_url": "https://api.github.com/repos/vuejs/vue/forks", 30 | "keys_url": "https://api.github.com/repos/vuejs/vue/keys{/key_id}", 31 | "collaborators_url": "https://api.github.com/repos/vuejs/vue/collaborators{/collaborator}", 32 | "teams_url": "https://api.github.com/repos/vuejs/vue/teams", 33 | "hooks_url": "https://api.github.com/repos/vuejs/vue/hooks", 34 | "issue_events_url": "https://api.github.com/repos/vuejs/vue/issues/events{/number}", 35 | "events_url": "https://api.github.com/repos/vuejs/vue/events", 36 | "assignees_url": "https://api.github.com/repos/vuejs/vue/assignees{/user}", 37 | "branches_url": "https://api.github.com/repos/vuejs/vue/branches{/branch}", 38 | "tags_url": "https://api.github.com/repos/vuejs/vue/tags", 39 | "blobs_url": "https://api.github.com/repos/vuejs/vue/git/blobs{/sha}", 40 | "git_tags_url": "https://api.github.com/repos/vuejs/vue/git/tags{/sha}", 41 | "git_refs_url": "https://api.github.com/repos/vuejs/vue/git/refs{/sha}", 42 | "trees_url": "https://api.github.com/repos/vuejs/vue/git/trees{/sha}", 43 | "statuses_url": "https://api.github.com/repos/vuejs/vue/statuses/{sha}", 44 | "languages_url": "https://api.github.com/repos/vuejs/vue/languages", 45 | "stargazers_url": "https://api.github.com/repos/vuejs/vue/stargazers", 46 | "contributors_url": "https://api.github.com/repos/vuejs/vue/contributors", 47 | "subscribers_url": "https://api.github.com/repos/vuejs/vue/subscribers", 48 | "subscription_url": "https://api.github.com/repos/vuejs/vue/subscription", 49 | "commits_url": "https://api.github.com/repos/vuejs/vue/commits{/sha}", 50 | "git_commits_url": "https://api.github.com/repos/vuejs/vue/git/commits{/sha}", 51 | "comments_url": "https://api.github.com/repos/vuejs/vue/comments{/number}", 52 | "issue_comment_url": "https://api.github.com/repos/vuejs/vue/issues/comments{/number}", 53 | "contents_url": "https://api.github.com/repos/vuejs/vue/contents/{+path}", 54 | "compare_url": "https://api.github.com/repos/vuejs/vue/compare/{base}...{head}", 55 | "merges_url": "https://api.github.com/repos/vuejs/vue/merges", 56 | "archive_url": "https://api.github.com/repos/vuejs/vue/{archive_format}{/ref}", 57 | "downloads_url": "https://api.github.com/repos/vuejs/vue/downloads", 58 | "issues_url": "https://api.github.com/repos/vuejs/vue/issues{/number}", 59 | "pulls_url": "https://api.github.com/repos/vuejs/vue/pulls{/number}", 60 | "milestones_url": "https://api.github.com/repos/vuejs/vue/milestones{/number}", 61 | "notifications_url": "https://api.github.com/repos/vuejs/vue/notifications{?since,all,participating}", 62 | "labels_url": "https://api.github.com/repos/vuejs/vue/labels{/name}", 63 | "releases_url": "https://api.github.com/repos/vuejs/vue/releases{/id}", 64 | "deployments_url": "https://api.github.com/repos/vuejs/vue/deployments", 65 | "created_at": "2013-07-29T03:24:51Z", 66 | "updated_at": "2018-01-28T19:22:08Z", 67 | "pushed_at": "2018-01-28T15:21:27Z", 68 | "git_url": "git://github.com/vuejs/vue.git", 69 | "ssh_url": "git@github.com:vuejs/vue.git", 70 | "clone_url": "https://github.com/vuejs/vue.git", 71 | "svn_url": "https://github.com/vuejs/vue", 72 | "homepage": "http://vuejs.org", 73 | "size": 22346, 74 | "stargazers_count": 81783, 75 | "watchers_count": 81783, 76 | "language": "JavaScript", 77 | "has_issues": true, 78 | "has_projects": true, 79 | "has_downloads": true, 80 | "has_wiki": false, 81 | "has_pages": false, 82 | "forks_count": 12013, 83 | "mirror_url": null, 84 | "archived": false, 85 | "open_issues_count": 124, 86 | "license": { 87 | "key": "mit", 88 | "name": "MIT License", 89 | "spdx_id": "MIT", 90 | "url": "https://api.github.com/licenses/mit" 91 | }, 92 | "forks": 12013, 93 | "open_issues": 124, 94 | "watchers": 81783, 95 | "default_branch": "dev", 96 | "organization": { 97 | "login": "vuejs", 98 | "id": 6128107, 99 | "avatar_url": "https://avatars1.githubusercontent.com/u/6128107?v=4", 100 | "gravatar_id": "", 101 | "url": "https://api.github.com/users/vuejs", 102 | "html_url": "https://github.com/vuejs", 103 | "followers_url": "https://api.github.com/users/vuejs/followers", 104 | "following_url": "https://api.github.com/users/vuejs/following{/other_user}", 105 | "gists_url": "https://api.github.com/users/vuejs/gists{/gist_id}", 106 | "starred_url": "https://api.github.com/users/vuejs/starred{/owner}{/repo}", 107 | "subscriptions_url": "https://api.github.com/users/vuejs/subscriptions", 108 | "organizations_url": "https://api.github.com/users/vuejs/orgs", 109 | "repos_url": "https://api.github.com/users/vuejs/repos", 110 | "events_url": "https://api.github.com/users/vuejs/events{/privacy}", 111 | "received_events_url": "https://api.github.com/users/vuejs/received_events", 112 | "type": "Organization", 113 | "site_admin": false 114 | }, 115 | "network_count": 12013, 116 | "subscribers_count": 4421 117 | } 118 | -------------------------------------------------------------------------------- /test/github-repository.test.ts: -------------------------------------------------------------------------------- 1 | import 'mocha'; 2 | import { expect } from 'chai'; 3 | import * as sinon from 'sinon'; 4 | 5 | import { GithubRepository } from '../src/github-repository'; 6 | 7 | // Increase timeout for AppVeyor 8 | const TIMEOUT = 500; 9 | 10 | describe('', () => { 11 | let component: GithubRepository; 12 | let stub: sinon.SinonStub; 13 | 14 | beforeEach(async () => { 15 | localStorage.clear(); 16 | const realFetch = window.fetch; 17 | stub = sinon.stub(window, 'fetch').callsFake(async (url: string) => { 18 | const ownerRepo = url.replace('https://api.github.com/repos/', ''); 19 | return realFetch(`./base/test/data/${ownerRepo}.json`); 20 | }); 21 | }); 22 | 23 | afterEach(() => { 24 | stub.restore(); 25 | }); 26 | 27 | describe('without properties', () => { 28 | beforeEach(() => { 29 | component = fixture(''); 30 | }); 31 | 32 | it('renders loading', () => { 33 | expect(component.$('#loader')).to.exist; 34 | expect(component.$('.animated-background')).to.exist; 35 | expect(component.$$('.background-masker').length).to.eq(8); 36 | }); 37 | }); 38 | 39 | describe('with owner-repo', () => { 40 | beforeEach(async () => { 41 | component = fixture(''); 42 | await sleep(TIMEOUT); 43 | }); 44 | 45 | it('renders the header', () => { 46 | const links = component.$$('#header a') as NodeListOf; 47 | expect(links.length).to.eq(3); 48 | expect(links[0].href).to.eq('https://github.com/abraham'); 49 | expect(links[0].innerText).to.eq('abraham'); 50 | expect(links[1].href).to.eq('https://github.com/abraham/twitter-status'); 51 | expect(links[1].innerText).to.eq('twitter-status'); 52 | expect(links[2].href).to.eq('https://github.com/abraham/twitter-status'); 53 | expect(links[2].querySelector('svg').getAttribute('aria-label')).to.eq('GitHub'); 54 | }); 55 | 56 | it('renders the description', () => { 57 | expect(component.$('#description').innerText).to.eq('Twitter Status Web Component npmjs.com/package/twitter-status'); 58 | }); 59 | 60 | it('renders the homepage', () => { 61 | const link = component.$('#homepage a') as HTMLAnchorElement; 62 | expect(link.href).to.eq('https://www.npmjs.com/package/twitter-status'); 63 | expect(link.innerText).to.eq('npmjs.com/package/twitter-status'); 64 | }); 65 | 66 | it('renders the counters', () => { 67 | const links = component.$$('#counters a') as NodeListOf; 68 | expect(links.length).to.eq(4); 69 | expect(links[0].href).to.eq('https://github.com/abraham/twitter-status/watchers'); 70 | expect(links[0].innerText.trim()).to.eq('Watchers\n1'); 71 | expect(links[1].href).to.eq('https://github.com/abraham/twitter-status/stargazers'); 72 | expect(links[1].innerText.trim()).to.eq('Stars\n5'); 73 | expect(links[2].href).to.eq('https://github.com/abraham/twitter-status/network'); 74 | expect(links[2].innerText.trim()).to.eq('Forks\n1'); 75 | expect(links[3].href).to.eq('https://github.com/abraham/twitter-status/issues'); 76 | expect(links[3].innerText.trim()).to.eq('Issues\n6'); 77 | }); 78 | 79 | it('renders clone', () => { 80 | expect(component.$('#clone pre').innerText).to.eq('git@github.com:abraham/twitter-status.git'); 81 | }); 82 | 83 | it('renders the footer', () => { 84 | const links = component.$$('#footer .item'); 85 | expect(links.length).to.eq(3); 86 | expect(links[0].innerText).to.include('TypeScript'); 87 | expect(links[1].innerText).to.include('Updated Jan 27'); 88 | expect(links[2].innerText).to.include('MIT License'); 89 | }); 90 | }); 91 | 92 | describe('with owner-repo for an empty project', () => { 93 | beforeEach(async () => { 94 | component = fixture(''); 95 | await sleep(TIMEOUT); 96 | }); 97 | 98 | it('renders the header', () => { 99 | const links = component.$$('#header a') as NodeListOf; 100 | expect(links.length).to.eq(3); 101 | expect(links[0].href).to.eq('https://github.com/abraham'); 102 | expect(links[0].innerText).to.eq('abraham'); 103 | expect(links[1].href).to.eq('https://github.com/abraham/empty'); 104 | expect(links[1].innerText).to.eq('empty'); 105 | expect(links[2].href).to.eq('https://github.com/abraham/empty'); 106 | expect(links[2].querySelector('svg').getAttribute('aria-label')).to.eq('GitHub'); 107 | }); 108 | 109 | it('renders the description', () => { 110 | expect(component.$('#description').innerText).to.eq(''); 111 | }); 112 | 113 | 114 | it('does not render homepage', () => { 115 | expect(component.$('#homepage')).to.be.null; 116 | }); 117 | 118 | it('renders the counters', () => { 119 | const links = component.$$('#counters a') as NodeListOf; 120 | expect(links.length).to.eq(4); 121 | expect(links[0].href).to.eq('https://github.com/abraham/empty/watchers'); 122 | expect(links[0].innerText.trim()).to.eq('Watchers\n1'); 123 | expect(links[1].href).to.eq('https://github.com/abraham/empty/stargazers'); 124 | expect(links[1].innerText.trim()).to.eq('Stars\n0'); 125 | expect(links[2].href).to.eq('https://github.com/abraham/empty/network'); 126 | expect(links[2].innerText.trim()).to.eq('Forks\n0'); 127 | expect(links[3].href).to.eq('https://github.com/abraham/empty/issues'); 128 | expect(links[3].innerText.trim()).to.eq('Issues\n0'); 129 | }); 130 | 131 | it('renders clone', () => { 132 | expect(component.$('#clone pre').innerText).to.eq('git@github.com:abraham/empty.git'); 133 | }); 134 | 135 | it('renders the footer', () => { 136 | const links = component.$$('#footer .item'); 137 | expect(links.length).to.eq(3); 138 | expect(links[0].innerText).to.include('Unknown language'); 139 | expect(links[1].innerText).to.include('Updated Jan 28 2017'); 140 | expect(links[2].innerText).to.include('Unknown license'); 141 | }); 142 | 143 | it('renders link decorators', () => { 144 | const linksCount = component.$$('a').length; 145 | expect(component.$$('a[target="_blank"]').length).to.eq(linksCount); 146 | expect(component.$$('a[rel="noopener"]').length).to.eq(linksCount); 147 | }); 148 | }); 149 | 150 | describe('GitHub API', () => { 151 | it('is requested', () => { 152 | window.fetch = sinon.spy(); 153 | component = fixture(''); 154 | expect((window.fetch as sinon.SinonSpy).calledWith('https://api.github.com/repos/abraham/twitter-status')).to.be.true; 155 | expect((window.fetch as sinon.SinonSpy).calledOnce).to.be.true; 156 | }); 157 | 158 | describe('with error response', () => { 159 | beforeEach(async () => { 160 | stub.restore(); 161 | const realFetch = window.fetch; 162 | stub = sinon.stub(window, 'fetch').callsFake(async () => { 163 | const error = await realFetch(`./base/test/data/error.json`); 164 | return new Response(await error.text(), { status: 403, statusText: 'Forbidden' }) 165 | }); 166 | component = fixture(''); 167 | await sleep(TIMEOUT); 168 | }); 169 | 170 | afterEach(() => { 171 | stub.restore(); 172 | }); 173 | 174 | it('renders error', () => { 175 | expect(component.$('#error')).to.exist; 176 | const text = `Error getting abraham/twitter-status details from from GitHub: 177 | "API rate limit exceeded for 71.90.116.85. (But here\'s the good news: Authenticated requests get a higher rate limit. Check out the documentation for more details.)"`; 178 | expect(component.$('#error').innerText).to.eq(text); 179 | }); 180 | }); 181 | 182 | describe('with recent cache', () => { 183 | beforeEach(async () => { 184 | const data = await fetch('https://api.github.com/repos/abraham/twitter-status'); 185 | const cache = { 186 | data: await data.json(), 187 | cachedAt: Date.now() - (23 * 60 * 60 * 1000), 188 | }; 189 | window.localStorage.setItem('github-repository_abraham/twitter-status_cache', JSON.stringify(cache)); 190 | }); 191 | 192 | it('is not requested', () => { 193 | window.fetch = sinon.spy(); 194 | component = fixture(''); 195 | expect((window.fetch as sinon.SinonSpy).calledWith('https://api.github.com/repos/abraham/twitter-status')).to.be.false; 196 | expect((window.fetch as sinon.SinonSpy).calledOnce).to.be.false; 197 | }); 198 | }); 199 | 200 | describe('with old cache', () => { 201 | beforeEach(async () => { 202 | const data = await fetch('https://api.github.com/repos/abraham/twitter-status'); 203 | const cache = { 204 | data: await data.json(), 205 | cachedAt: Date.now() - (25 * 60 * 60 * 1000), 206 | }; 207 | window.localStorage.setItem('github-repository_abraham/twitter-status_cache', JSON.stringify(cache)); 208 | }); 209 | 210 | it('is requested', () => { 211 | window.fetch = sinon.spy(); 212 | component = fixture(''); 213 | expect((window.fetch as sinon.SinonSpy).calledWith('https://api.github.com/repos/abraham/twitter-status')).to.be.true; 214 | expect((window.fetch as sinon.SinonSpy).calledOnce).to.be.true; 215 | setTimeout(() => { 216 | const cache = JSON.parse(window.localStorage.getItem('github-repository_abraham/twitter-status_cache')); 217 | expect(cache.cachedAt).to.be.within(Date.now() - 1000, Date.now() + 1000); 218 | }, TIMEOUT); 219 | }); 220 | }); 221 | }); 222 | 223 | 224 | describe('slot', () => { 225 | beforeEach(async () => { 226 | component = fixture(` 227 | 228 | Twitter Status embed preview 229 | Version Status 230 | macOS Build Status 231 | 232 | `); 233 | await sleep(TIMEOUT); 234 | }); 235 | 236 | it('badges are rendered', () => { 237 | const assignedNodes = (component.$('#badges-slot slot') as HTMLSlotElement).assignedNodes(); 238 | expect(assignedNodes.length).to.eq(2); 239 | expect((assignedNodes[0] as HTMLAnchorElement).href).to.eq('https://npmjs.com/package/twitter-status'); 240 | }); 241 | 242 | it('images are rendered', () => { 243 | const assignedNodes = (component.$('#images-slot') as HTMLSlotElement).assignedNodes(); 244 | expect(assignedNodes.length).to.eq(1); 245 | expect((assignedNodes[0] as HTMLImageElement).src).to.eq('https://raw.githubusercontent.com/abraham/twitter-status/master/images/simple.png'); 246 | }); 247 | }); 248 | }); 249 | 250 | function fixture(tag: string): GithubRepository { 251 | function fixtureContainer(): HTMLElement { 252 | let div = document.createElement('div'); 253 | div.classList.add('fixture'); 254 | return div; 255 | } 256 | let fixture = document.body.querySelector('.fixture') || document.body.appendChild(fixtureContainer()); 257 | fixture.innerHTML = tag; 258 | return fixture.children[0] as GithubRepository; 259 | } 260 | 261 | function sleep(ms: number): Promise<{}> { 262 | return new Promise(resolve => setTimeout(resolve, ms)); 263 | } 264 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.production", 3 | "include": [ 4 | "src", 5 | "test" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.production.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "emitDecoratorMetadata": true, 5 | "experimentalDecorators": true, 6 | "inlineSources": true, 7 | "lib": ["esnext", "dom"], 8 | "module": "esnext", 9 | "moduleResolution": "node", 10 | "noFallthroughCasesInSwitch": true, 11 | "noImplicitAny": true, 12 | "noImplicitReturns": true, 13 | "noUnusedParameters": true, 14 | "outDir": "dist", 15 | "skipLibCheck": true, 16 | "sourceMap": true, 17 | "sourceRoot": "src", 18 | "strict": true, 19 | "target": "esnext" 20 | }, 21 | "include": [ 22 | "src" 23 | ], 24 | "exclude": [ 25 | "node_modules", 26 | "dist" 27 | ] 28 | } 29 | --------------------------------------------------------------------------------