more readme updates
This commit is contained in:
parent
4b41ab1348
commit
f8a0516bab
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
.sass-cache/
|
||||
_site/
|
||||
|
16
_layouts/default.html
Normal file
16
_layouts/default.html
Normal file
@ -0,0 +1,16 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>{{ page.title }}</title>
|
||||
<link href='http://fonts.googleapis.com/css?family=Neuton' rel='stylesheet' type='text/css'>
|
||||
<link href='http://fonts.googleapis.com/css?family=Crimson+Text' rel='stylesheet' type='text/css'>
|
||||
<link rel="stylesheet" href="/stylesheets/screen.css" type="text/css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div id="inner-container">
|
||||
{{ content }}
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
26
config.rb
Normal file
26
config.rb
Normal file
@ -0,0 +1,26 @@
|
||||
require 'susy'
|
||||
# Require any additional compass plugins here.
|
||||
|
||||
|
||||
# Set this to the root of your project when deployed:
|
||||
http_path = "/"
|
||||
css_dir = "stylesheets"
|
||||
sass_dir = "sass"
|
||||
images_dir = "images"
|
||||
javascripts_dir = "javascripts"
|
||||
|
||||
# You can select your preferred output style here (can be overridden via the command line):
|
||||
# output_style = :expanded or :nested or :compact or :compressed
|
||||
|
||||
# To enable relative paths to assets via compass helper functions. Uncomment:
|
||||
# relative_assets = true
|
||||
|
||||
# To disable debugging comments that display the original location of your selectors. Uncomment:
|
||||
# line_comments = false
|
||||
|
||||
|
||||
# If you prefer the indented syntax, you might want to regenerate this
|
||||
# project again passing --syntax sass, or you can uncomment this:
|
||||
# preferred_syntax = :sass
|
||||
# and then run:
|
||||
# sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass
|
BIN
images/colored-output.png
Normal file
BIN
images/colored-output.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
BIN
images/f5.png
Normal file
BIN
images/f5.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 90 KiB |
177
index.md
177
index.md
@ -1,23 +1,25 @@
|
||||
---
|
||||
title: jasmine-headless-webkit -- The fastest way to run your Jasmine specs!
|
||||
layout: default
|
||||
---
|
||||
|
||||
# `jasmine-headless-webkit`
|
||||
# Jasmine Headless WebKit
|
||||
## Run your Jasmine specs at sonic boom speed!
|
||||
|
||||
<img src="images/f5.png" alt="Colored Output" />
|
||||
[Jasmine](http://pivotal.github.com/jasmine/) is great. I love it. But running Jasmine when you need to test code that will run
|
||||
in a browser environment can be problematic and slow:
|
||||
|
||||
* The Jasmine gem's server makes getting up and testing very fast, but F5-ing your browser for each test run is distracting.
|
||||
* Jasmine CI uses Selenium, which speeds up the process a bit, but you're still rendering pixels in a browser, so it's slower than it should be.
|
||||
* Node.js, EnvJS, and Rhino solutions for running Jasmine are great for anything that will never run in a real browser, since browser simulators are not true browsers.
|
||||
* The [Jasmine gem](https://github.com/pivotal/jasmine-gem)'s server makes getting up and testing very fast, but F5-ing your browser for each test run is distracting.
|
||||
* Jasmine CI uses Selenium, which speeds up the process a bit, but you're still rendering pixels in a browser, albeit with the option of rendering those pixels in a lot of different browsers at once.
|
||||
* Node.js, EnvJS, and Rhino solutions for running Jasmine are great for anything that will never run in a real browser. I'm a big believer of running code destined for a browser in a browser itself, not a simulator.
|
||||
|
||||
But there's a solution for browser-based testing, and it dovetails perfectly into the Jasmine gem's already established protocols.
|
||||
But there's a solution for fast, accurate browser-based testing, using one of the most popular browser cores, and it dovetails perfectly into the Jasmine gem's already established protocols.
|
||||
|
||||
## Enter `jasmine-headless-webkit`
|
||||
|
||||
`jasmine-headless-webkit` uses the Qt WebKit widget to run your specs without needing to render a pixel. It's nearly
|
||||
as fast as running in a JavaScript engine like Node.js, and since it's a real browser environment, all the modules
|
||||
`jasmine-headless-webkit` uses the [Qt WebKit widget](http://trac.webkit.org/wiki/QtWebKit) to run your specs without needing to render a pixel. It's nearly
|
||||
as fast as running in a JavaScript engine like Node.js, and, since it's a real browser environment, all the modules
|
||||
you would normally use, like jQuery and Backbone, work without any modifications. If you write your tests correctly,
|
||||
they'll even work when running in the Jasmine gem's server with no changes to your code.
|
||||
|
||||
@ -27,7 +29,7 @@ they'll even work when running in the Jasmine gem's server with no changes to yo
|
||||
* It compiles [CoffeeScript](http://jashkenas.github.com/coffee-script/), both for your tests and for your application logic.
|
||||
* It can be configured like RSpec, and its output is very similar to RSpec's output, so you don't need to learn too much new stuff to use and integrate it.
|
||||
|
||||
## How to use it
|
||||
## How do I use this wonderful toy?
|
||||
|
||||
You can use it standalone:
|
||||
|
||||
@ -43,6 +45,23 @@ to use the Jasmine gem:
|
||||
gem install jasmine
|
||||
jasmine init
|
||||
|
||||
### What do I need to get it working?
|
||||
|
||||
Installation requires Qt 4.7. The Internets will tell you how to get that for your particular environment.
|
||||
`jasmine-headless-webkit` has been tested in the following environments:
|
||||
|
||||
* Mac OS X 10.6, with MacPorts Qt and Nokia Qt.mpkg
|
||||
* Kubuntu 10.10
|
||||
|
||||
If it works in yours, [leave me a message on GitHub](https://github.com/johnbintz) or
|
||||
[fork this site](https://github.com/johnbintz/jasmine-headless-webkit/tree/gh-pages) and add your setup.
|
||||
|
||||
### How does it work?
|
||||
|
||||
`jasmine-headless-webkit` generates a static HTML file that includes the Jasmine JavaScript library from the Jasmine
|
||||
gem, your application and spec files, and any helpers you may need. The runner then creates a WebKit widget that
|
||||
loads the HTML file, runs the tests, and grabs the results of the test to show back to you. Awesome!
|
||||
|
||||
`jasmine-headless-webkit` uses the same `jasmine.yml` file that the Jasmine gem file uses to define where particular
|
||||
files for the testing process are located:
|
||||
|
||||
@ -59,27 +78,149 @@ src_files:
|
||||
helpers:
|
||||
- helpers/**/*.{js,coffee}
|
||||
spec_files:
|
||||
- **/*[Ss]pec.{js,coffee}
|
||||
- "**/*[Ss]pec.{js,coffee}"
|
||||
src_dir:
|
||||
spec_dir: spec/javascripts
|
||||
{% endhighlight %}
|
||||
|
||||
### *.coffee in my jasmine.yml file?!
|
||||
It also brings in the same copy of the Jasmine library that the Jasmine gem includes, so if you're testing in both environments,
|
||||
you're guaranteed to get the same results in your tests.
|
||||
|
||||
Yes, `jasmine-headless-webkit` will support *.coffee files in `jasmine.yml`, while the normal Jasmine server currently
|
||||
#### `*.coffee` in my `jasmine.yml` file?!
|
||||
|
||||
Yes, `jasmine-headless-webkit` will support `*.coffee` files in `jasmine.yml`, which the normal Jasmine server currently
|
||||
does not support out of the box. Once there's official support, you'll be able to easily switch between `jasmine-headless-webkit`
|
||||
and the Jasmine test server when you're using CoffeeScript.
|
||||
and the Jasmine test server when you're using CoffeeScript. CoffeeScript files are compiled and injected into the generated HTML
|
||||
files.
|
||||
|
||||
#### Server interaction
|
||||
|
||||
Since there's no Jasmine server running, there's no way to grab test files from the filesystem via Ajax.
|
||||
If you need to test server interaction, do one of the following:
|
||||
|
||||
* Stub your server responses using [Sinon.JS](http://sinonjs.org/).
|
||||
* Use [PhantomJS](http://www.phantomjs.org/) against a running copy of a Jasmine server, instead of this project.
|
||||
|
||||
#### What else works?
|
||||
|
||||
`alert()` and `confirm()` work, though the latter always returns `true`. You should be mocking calls to `confirm()`,
|
||||
of course:
|
||||
|
||||
{% highlight js %}
|
||||
spyOn(window, 'confirm').andReturn(false)
|
||||
{% endhighlight %}
|
||||
|
||||
`console.log()` also works, though it's just a wrapper around `JSON.stringify()`. This means that cyclical objects, like HTML
|
||||
elements, can't be directly serialized (yet). Use jQuery to help you retrieve the HTML:
|
||||
|
||||
{% highlight js %}
|
||||
console.log($('#element').parent().html())
|
||||
{% endhighlight %}
|
||||
|
||||
## Running the runner
|
||||
|
||||
jasmine-headless-webkit [ -c / --colors ]
|
||||
[ --no-colors ]
|
||||
[ --keep ]
|
||||
[ -j / --jasmine-config <path to jasmine.yml> ]
|
||||
<spec files to run>
|
||||
{% highlight bash %}
|
||||
jasmine-headless-webkit [ -c / --colors ]
|
||||
[ --no-colors ]
|
||||
[ --keep ]
|
||||
[ -j / --jasmine-config <path to jasmine.yml> ]
|
||||
<spec files to run>
|
||||
{% endhighlight %}
|
||||
|
||||
The runner will return one of three exit codes:
|
||||
|
||||
* __0__ means your tests passed sucessfully.
|
||||
* __1__ means you had a failure in your tests.
|
||||
* __2__ means your tests passed, but you used `console.log()` somewhere.
|
||||
|
||||
### Setting default options
|
||||
|
||||
Much like RSpec, you can define the default options for each run of the runner. Place your global options into a
|
||||
`~/.jasmine-headless-webkit` file and your per-project settings in a `.jasmine-headless-webkit` file at the root of
|
||||
the project.
|
||||
|
||||
### Coloring the output
|
||||
|
||||
`jasmine-headless-webkit` will not color output by default. This makes it easier to integrate with CI servers. If you want
|
||||
colored output, use the `-c` flag.
|
||||
colored output, use the `-c` flag. With colored output, your tests will look like this:
|
||||
|
||||
<img class="large" src="images/colored-output.png" alt="Colored Output" />
|
||||
|
||||
If you have colors turned on globally, you can turn them off per-project or per-run with `--no-colors`.
|
||||
|
||||
### Preserving compiled output on errors
|
||||
|
||||
CoffeeScript logic errors can be hard to track down. Keep the generated HTML files with the `--keep` flag and you'll
|
||||
get `specrunner.$$.html` files in your working directory.
|
||||
|
||||
### Using a different `jasmine.yml` file
|
||||
|
||||
If for some reason you're not using the default path for a `jasmine.yml` file (which is `spec/javascripts/support/jasmine.yml`),
|
||||
you can provide that path with `-j`.
|
||||
|
||||
### Running only certain spec files
|
||||
|
||||
By default, if no files are passed into `jasmine-headless-webkit`, all possible spec files in the `spec_files` definition
|
||||
will be run. You can limit the run to only certain files by passing those to `jasmine-headless-webkit`:
|
||||
|
||||
{% highlight bash %}
|
||||
jasmine-headless-webkit spec/javascripts/models/node_viewer.coffee
|
||||
{% endhighlight %}
|
||||
|
||||
## Automated testing during development
|
||||
|
||||
`jasmine-headless-webkit` works best when it's running all the time, re-running tests when you update the appropriate files.
|
||||
Support for Autotest is built-in. All you need to do is create a `.jasmine-headless-webkit` file in your project directory
|
||||
and Autotest will pick up that you want to use it for Jasmine. _(this only works by itself or with RSpec at the moment)_
|
||||
|
||||
You can also use it with watchr, which is what I do now. Here's the watchr script I use to run both RSpec and
|
||||
`jasmine-headless-webkit`:
|
||||
|
||||
<script src="https://gist.github.com/965115.js?file=test.watchr.rb"></script>
|
||||
|
||||
## Rake tasks
|
||||
|
||||
You can create a Rake task for your headless Jasmine specs:
|
||||
|
||||
{% highlight ruby %}
|
||||
require 'jasmine/headless/task'
|
||||
|
||||
Jasmine::Headless::Task.new('jasmine:headless') do |t|
|
||||
t.colors = true
|
||||
t.keep_on_error = true
|
||||
t.jasmine_config = 'this/is/the/path.yml'
|
||||
end
|
||||
{% endhighlight %}
|
||||
|
||||
## I have a problem or helpful suggestion, good sir.
|
||||
|
||||
Here's what you can do:
|
||||
|
||||
* Leave a ticket on [the Issues tracker](https://github.com/johnbintz/jasmine-headless-webkit/issues).
|
||||
* [Fork'n'fix the code](https://github.com/johnbintz/jasmine-headless-webkit). Feel free to add a bunch of tests, too. I cowboyed this project when starting it, and I'm slowly getting back to being a good boy.
|
||||
* Ping me on [Twitter](http://twitter.com/johnbintz) or on [GitHub](https://github.com/johnbintz).
|
||||
|
||||
## Credits & License
|
||||
|
||||
* Copyright (c) 2011 John Bintz
|
||||
* Original Qt WebKit runner Copyright (c) 2010 Sencha Inc.
|
||||
* Jasmine JavaScript library Copyright (c) 2008-2011 Pivotal Labs
|
||||
|
||||
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:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
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.
|
||||
|
||||
|
35
sass/_base.scss
Normal file
35
sass/_base.scss
Normal file
@ -0,0 +1,35 @@
|
||||
// Imports -------------------------------------------------------------------
|
||||
|
||||
@import "susy";
|
||||
|
||||
// Grid ----------------------------------------------------------------------
|
||||
// http://colorschemedesigner.com/#0F31Tblebw0w0
|
||||
|
||||
$total-cols : 12;
|
||||
$col-width : 3em;
|
||||
$gutter-width : 1em;
|
||||
$side-gutter-width : $gutter-width;
|
||||
|
||||
$show-grid-backgrounds : false;
|
||||
|
||||
$page-background-color: #554B3E;
|
||||
$content-background-color: #B8A287;
|
||||
$text-color: #493318;
|
||||
|
||||
$link-color: #171233;
|
||||
$link-visited-color: #2F2D3B;
|
||||
|
||||
$header-color: #715F49;
|
||||
$other-header-color: #36384E;
|
||||
$code-background-color: #113422;
|
||||
$code-foreground-color: #B6868A;
|
||||
|
||||
// Code Coloring
|
||||
// http://colorschemedesigner.com/#5.31Tblebw0w0
|
||||
|
||||
$scalar-code-color: #E68917;
|
||||
$indicator-code-color: #78A39D;
|
||||
$variable-code-color: #ABB592;
|
||||
$primitive-code-color: #A7B585;
|
||||
$class-code-color: #DE1629;
|
||||
$object-code-color: #CEF56B;
|
107
sass/screen.scss
Normal file
107
sass/screen.scss
Normal file
@ -0,0 +1,107 @@
|
||||
// Imports -------------------------------------------------------------------
|
||||
|
||||
@import "base";
|
||||
|
||||
/* Layout ------------------------------------------------------------------*/
|
||||
|
||||
body {
|
||||
background-color: $page-background-color;
|
||||
margin: 0;
|
||||
|
||||
#container {
|
||||
background-color: $content-background-color;
|
||||
@include container;
|
||||
@include susy-grid-background;
|
||||
|
||||
#inner-container {
|
||||
@include columns($total-cols);
|
||||
@include alpha;
|
||||
color: $text-color;
|
||||
|
||||
a {
|
||||
color: $link-color;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: $link-visited-color;
|
||||
}
|
||||
|
||||
img.large {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
img[src*="f5.png"] {
|
||||
@include float-right;
|
||||
}
|
||||
|
||||
div, p, li, span {
|
||||
font-family: 'Crimson Text', 'Georgia', sans-serif
|
||||
}
|
||||
|
||||
pre, code, code > span, pre > div > span {
|
||||
font-family: 'Courier';
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: $gutter-width;
|
||||
background-color: $code-background-color;
|
||||
color: $code-foreground-color;
|
||||
}
|
||||
|
||||
p > code {
|
||||
color: $text-color - #222;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4 {
|
||||
font-family: 'Neuton', 'Helvetica', sans-serif
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
font-size: 3.25em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.gist-highlight {
|
||||
font-size: 75%
|
||||
}
|
||||
|
||||
.yaml {
|
||||
.l-Scalar-Plain {
|
||||
color: $scalar-code-color;
|
||||
}
|
||||
|
||||
.p-Indicator {
|
||||
color: $indicator-code-color;
|
||||
}
|
||||
}
|
||||
|
||||
.js, .ruby {
|
||||
.nx {
|
||||
color: $indicator-code-color;
|
||||
}
|
||||
|
||||
.s1 {
|
||||
color: $scalar-code-color;
|
||||
}
|
||||
|
||||
.nb {
|
||||
color: $variable-code-color;
|
||||
}
|
||||
|
||||
.kc, .kp {
|
||||
color: $primitive-code-color;
|
||||
}
|
||||
|
||||
.no {
|
||||
color: $class-code-color;
|
||||
}
|
||||
|
||||
.n {
|
||||
color: $object-code-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
114
stylesheets/screen.css
Normal file
114
stylesheets/screen.css
Normal file
@ -0,0 +1,114 @@
|
||||
/* Layout ------------------------------------------------------------------*/
|
||||
/* line 7, ../sass/screen.scss */
|
||||
body {
|
||||
background-color: #554b3e;
|
||||
margin: 0;
|
||||
}
|
||||
/* line 11, ../sass/screen.scss */
|
||||
body #container {
|
||||
background-color: #b8a287;
|
||||
*zoom: 1;
|
||||
margin: auto;
|
||||
width: 49em;
|
||||
max-width: 100%;
|
||||
}
|
||||
/* line 22, ../../../.rvm/gems/ruby-1.9.2-p180/gems/compass-0.11.1/frameworks/compass/stylesheets/compass/utilities/general/_clearfix.scss */
|
||||
body #container:after {
|
||||
content: "\0020";
|
||||
display: block;
|
||||
height: 0;
|
||||
clear: both;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
}
|
||||
/* line 16, ../sass/screen.scss */
|
||||
body #container #inner-container {
|
||||
display: inline;
|
||||
float: left;
|
||||
width: 95.918%;
|
||||
margin-right: 2.041%;
|
||||
margin-left: 2.041%;
|
||||
color: #493318;
|
||||
}
|
||||
/* line 21, ../sass/screen.scss */
|
||||
body #container #inner-container a {
|
||||
color: #171233;
|
||||
}
|
||||
/* line 25, ../sass/screen.scss */
|
||||
body #container #inner-container a:visited {
|
||||
color: #2f2d3b;
|
||||
}
|
||||
/* line 29, ../sass/screen.scss */
|
||||
body #container #inner-container img.large {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
/* line 34, ../sass/screen.scss */
|
||||
body #container #inner-container img[src*="f5.png"] {
|
||||
display: inline;
|
||||
float: right;
|
||||
}
|
||||
/* line 38, ../sass/screen.scss */
|
||||
body #container #inner-container div, body #container #inner-container p, body #container #inner-container li, body #container #inner-container span {
|
||||
font-family: 'Crimson Text', 'Georgia', sans-serif;
|
||||
}
|
||||
/* line 42, ../sass/screen.scss */
|
||||
body #container #inner-container pre, body #container #inner-container code, body #container #inner-container code > span, body #container #inner-container pre > div > span {
|
||||
font-family: 'Courier';
|
||||
}
|
||||
/* line 46, ../sass/screen.scss */
|
||||
body #container #inner-container pre {
|
||||
padding: 1em;
|
||||
background-color: #113422;
|
||||
color: #b6868a;
|
||||
}
|
||||
/* line 52, ../sass/screen.scss */
|
||||
body #container #inner-container p > code {
|
||||
color: #271100;
|
||||
}
|
||||
/* line 56, ../sass/screen.scss */
|
||||
body #container #inner-container h1, body #container #inner-container h2, body #container #inner-container h3, body #container #inner-container h4 {
|
||||
font-family: 'Neuton', 'Helvetica', sans-serif;
|
||||
}
|
||||
/* line 60, ../sass/screen.scss */
|
||||
body #container #inner-container h1 {
|
||||
text-align: center;
|
||||
font-size: 3.25em;
|
||||
margin: 0;
|
||||
}
|
||||
/* line 66, ../sass/screen.scss */
|
||||
body #container #inner-container .gist-highlight {
|
||||
font-size: 75%;
|
||||
}
|
||||
/* line 71, ../sass/screen.scss */
|
||||
body #container #inner-container .yaml .l-Scalar-Plain {
|
||||
color: #e68917;
|
||||
}
|
||||
/* line 75, ../sass/screen.scss */
|
||||
body #container #inner-container .yaml .p-Indicator {
|
||||
color: #78a39d;
|
||||
}
|
||||
/* line 81, ../sass/screen.scss */
|
||||
body #container #inner-container .js .nx, body #container #inner-container .ruby .nx {
|
||||
color: #78a39d;
|
||||
}
|
||||
/* line 85, ../sass/screen.scss */
|
||||
body #container #inner-container .js .s1, body #container #inner-container .ruby .s1 {
|
||||
color: #e68917;
|
||||
}
|
||||
/* line 89, ../sass/screen.scss */
|
||||
body #container #inner-container .js .nb, body #container #inner-container .ruby .nb {
|
||||
color: #abb592;
|
||||
}
|
||||
/* line 93, ../sass/screen.scss */
|
||||
body #container #inner-container .js .kc, body #container #inner-container .js .kp, body #container #inner-container .ruby .kc, body #container #inner-container .ruby .kp {
|
||||
color: #a7b585;
|
||||
}
|
||||
/* line 97, ../sass/screen.scss */
|
||||
body #container #inner-container .js .no, body #container #inner-container .ruby .no {
|
||||
color: #de1629;
|
||||
}
|
||||
/* line 101, ../sass/screen.scss */
|
||||
body #container #inner-container .js .n, body #container #inner-container .ruby .n {
|
||||
color: #cef56b;
|
||||
}
|
Loading…
Reference in New Issue
Block a user