Merge branch 'dirbrowser-issue-1334' of https://github.com/Kingdread/qutebrowser into Kingdread-dirbrowser-issue-1334
This commit is contained in:
commit
6c7e2492e9
@ -57,9 +57,29 @@ def is_root(directory):
|
|||||||
Return:
|
Return:
|
||||||
Whether the directory is a root directory or not.
|
Whether the directory is a root directory or not.
|
||||||
"""
|
"""
|
||||||
|
# If you're curious as why this works:
|
||||||
|
# dirname('/') = '/'
|
||||||
|
# dirname('/home') = '/'
|
||||||
|
# dirname('/home/') = '/home'
|
||||||
|
# dirname('/home/foo') = '/home'
|
||||||
|
# basically, for files (no trailing slash) it removes the file part, and
|
||||||
|
# for directories, it removes the trailing slash, so the only way for this
|
||||||
|
# to be equal is if the directory is the root directory.
|
||||||
return os.path.dirname(directory) == directory
|
return os.path.dirname(directory) == directory
|
||||||
|
|
||||||
|
|
||||||
|
def parent_dir(directory):
|
||||||
|
"""Return the parent directory for the given directory.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
directory: The path to the directory.
|
||||||
|
|
||||||
|
Return:
|
||||||
|
The path to the parent directory.
|
||||||
|
"""
|
||||||
|
return os.path.normpath(os.path.join(directory, os.pardir))
|
||||||
|
|
||||||
|
|
||||||
def dirbrowser_html(path):
|
def dirbrowser_html(path):
|
||||||
"""Get the directory browser web page.
|
"""Get the directory browser web page.
|
||||||
|
|
||||||
@ -74,14 +94,14 @@ def dirbrowser_html(path):
|
|||||||
if is_root(path):
|
if is_root(path):
|
||||||
parent = None
|
parent = None
|
||||||
else:
|
else:
|
||||||
parent = os.path.dirname(path)
|
parent = parent_dir(path)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
all_files = os.listdir(path)
|
all_files = os.listdir(path)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
html = jinja.render('error.html',
|
html = jinja.render('error.html',
|
||||||
title="Error while reading directory",
|
title="Error while reading directory",
|
||||||
url='file://{}'.format(path), error=str(e),
|
url='file:///{}'.format(path), error=str(e),
|
||||||
icon='')
|
icon='')
|
||||||
return html.encode('UTF-8', errors='xmlcharrefreplace')
|
return html.encode('UTF-8', errors='xmlcharrefreplace')
|
||||||
|
|
||||||
|
@ -46,21 +46,21 @@ ul.files > li {
|
|||||||
<p id="dirbrowserTitleText">Browse directory: {{url}}</p>
|
<p id="dirbrowserTitleText">Browse directory: {{url}}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if parent %}
|
{% if parent is not none %}
|
||||||
<ul class="parent">
|
<ul class="parent">
|
||||||
<li><a href="{{parent}}">..</a></li>
|
<li><a href="{{ file_url(parent) }}">..</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<ul class="folders">
|
<ul class="folders">
|
||||||
{% for item in directories %}
|
{% for item in directories %}
|
||||||
<li><a href="file://{{item.absname}}">{{item.name}}</a></li>
|
<li><a href="{{ file_url(item.absname) }}">{{item.name}}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<ul class="files">
|
<ul class="files">
|
||||||
{% for item in files %}
|
{% for item in files %}
|
||||||
<li><a href="file://{{item.absname}}">{{item.name}}</a></li>
|
<li><a href="{{ file_url(item.absname) }}">{{item.name}}</a></li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -74,6 +74,15 @@ def resource_url(path):
|
|||||||
return QUrl.fromLocalFile(image).toString(QUrl.FullyEncoded)
|
return QUrl.fromLocalFile(image).toString(QUrl.FullyEncoded)
|
||||||
|
|
||||||
|
|
||||||
|
def file_url(path):
|
||||||
|
"""Return a file:// url (as string) to the given local path.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
path: The absolute path to the local file
|
||||||
|
"""
|
||||||
|
return QUrl.fromLocalFile(path).toString(QUrl.FullyEncoded)
|
||||||
|
|
||||||
|
|
||||||
def render(template, **kwargs):
|
def render(template, **kwargs):
|
||||||
"""Render the given template and pass the given arguments to it."""
|
"""Render the given template and pass the given arguments to it."""
|
||||||
try:
|
try:
|
||||||
@ -85,5 +94,7 @@ def render(template, **kwargs):
|
|||||||
tb = traceback.format_exc()
|
tb = traceback.format_exc()
|
||||||
return err_template.format(pagename=template, traceback=tb)
|
return err_template.format(pagename=template, traceback=tb)
|
||||||
|
|
||||||
|
|
||||||
_env = jinja2.Environment(loader=Loader('html'), autoescape=_guess_autoescape)
|
_env = jinja2.Environment(loader=Loader('html'), autoescape=_guess_autoescape)
|
||||||
_env.globals['resource_url'] = resource_url
|
_env.globals['resource_url'] = resource_url
|
||||||
|
_env.globals['file_url'] = file_url
|
||||||
|
11
tests/integration/data/click_element.html
Normal file
11
tests/integration/data/click_element.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>quteprocess.click_element test</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<span onclick='console.log("click_element clicked")'>Test Element</span>
|
||||||
|
<span onclick='console.log("click_element special chars")'>"Don't", he shouted</span>
|
||||||
|
<span>Duplicate</span>
|
||||||
|
<span>Duplicate</span>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -37,6 +37,7 @@ from PyQt5.QtCore import pyqtSignal, QUrl
|
|||||||
import testprocess
|
import testprocess
|
||||||
from qutebrowser.misc import ipc
|
from qutebrowser.misc import ipc
|
||||||
from qutebrowser.utils import log, utils
|
from qutebrowser.utils import log, utils
|
||||||
|
from qutebrowser.browser import webelem
|
||||||
from helpers import utils as testutils
|
from helpers import utils as testutils
|
||||||
|
|
||||||
|
|
||||||
@ -253,9 +254,14 @@ class QuteProc(testprocess.Process):
|
|||||||
path if path != '/' else '')
|
path if path != '/' else '')
|
||||||
|
|
||||||
def wait_for_js(self, message):
|
def wait_for_js(self, message):
|
||||||
"""Wait for the given javascript console message."""
|
"""Wait for the given javascript console message.
|
||||||
self.wait_for(category='js', function='javaScriptConsoleMessage',
|
|
||||||
message='[*] {}'.format(message))
|
Return:
|
||||||
|
The LogLine.
|
||||||
|
"""
|
||||||
|
return self.wait_for(category='js',
|
||||||
|
function='javaScriptConsoleMessage',
|
||||||
|
message='[*] {}'.format(message))
|
||||||
|
|
||||||
def _is_error_logline(self, msg):
|
def _is_error_logline(self, msg):
|
||||||
"""Check if the given LogLine is some kind of error message."""
|
"""Check if the given LogLine is some kind of error message."""
|
||||||
@ -348,10 +354,14 @@ class QuteProc(testprocess.Process):
|
|||||||
def open_path(self, path, *, new_tab=False, new_window=False, port=None,
|
def open_path(self, path, *, new_tab=False, new_window=False, port=None,
|
||||||
https=False):
|
https=False):
|
||||||
"""Open the given path on the local webserver in qutebrowser."""
|
"""Open the given path on the local webserver in qutebrowser."""
|
||||||
|
url = self.path_to_url(path, port=port, https=https)
|
||||||
|
self.open_url(url, new_tab=new_tab, new_window=new_window)
|
||||||
|
|
||||||
|
def open_url(self, url, *, new_tab=False, new_window=False):
|
||||||
|
"""Open the given url in qutebrowser."""
|
||||||
if new_tab and new_window:
|
if new_tab and new_window:
|
||||||
raise ValueError("new_tab and new_window given!")
|
raise ValueError("new_tab and new_window given!")
|
||||||
|
|
||||||
url = self.path_to_url(path, port=port, https=https)
|
|
||||||
if new_tab:
|
if new_tab:
|
||||||
self.send_cmd(':open -t ' + url)
|
self.send_cmd(':open -t ' + url)
|
||||||
elif new_window:
|
elif new_window:
|
||||||
@ -418,6 +428,57 @@ class QuteProc(testprocess.Process):
|
|||||||
"""Press the given keys using :fake-key."""
|
"""Press the given keys using :fake-key."""
|
||||||
self.send_cmd(':fake-key -g "{}"'.format(keys))
|
self.send_cmd(':fake-key -g "{}"'.format(keys))
|
||||||
|
|
||||||
|
def click_element(self, text):
|
||||||
|
"""Click the element with the given text."""
|
||||||
|
# Use Javascript and XPath to find the right element, use console.log
|
||||||
|
# to return an error (no element found, ambiguous element)
|
||||||
|
script = (
|
||||||
|
'var _es = document.evaluate(\'//*[text()={text}]\', document, '
|
||||||
|
'null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);'
|
||||||
|
'if (_es.snapshotLength == 0) {{ console.log("qute:no elems"); }} '
|
||||||
|
'else if (_es.snapshotLength > 1) {{ console.log("qute:ambiguous '
|
||||||
|
'elems") }} '
|
||||||
|
'else {{ console.log("qute:okay"); _es.snapshotItem(0).click() }}'
|
||||||
|
).format(text=webelem.javascript_escape(_xpath_escape(text)))
|
||||||
|
self.send_cmd(':jseval ' + script)
|
||||||
|
message = self.wait_for_js('qute:*').message
|
||||||
|
if message.endswith('qute:no elems'):
|
||||||
|
raise ValueError('No element with {!r} found'.format(text))
|
||||||
|
elif message.endswith('qute:ambiguous elems'):
|
||||||
|
raise ValueError('Element with {!r} is not unique'.format(text))
|
||||||
|
elif not message.endswith('qute:okay'):
|
||||||
|
raise ValueError('Invalid response from qutebrowser: {}'
|
||||||
|
.format(message))
|
||||||
|
|
||||||
|
|
||||||
|
def _xpath_escape(text):
|
||||||
|
"""Escape a string to be used in an XPath expression.
|
||||||
|
|
||||||
|
The resulting string should still be escaped with javascript_escape, to
|
||||||
|
prevent javascript from interpreting the quotes.
|
||||||
|
|
||||||
|
This function is needed because XPath does not provide any character
|
||||||
|
escaping mechanisms, so to get the string
|
||||||
|
"I'm back", he said
|
||||||
|
you have to use concat like
|
||||||
|
concat('"I', "'m back", '", he said')
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text: Text to escape
|
||||||
|
|
||||||
|
Return:
|
||||||
|
The string "escaped" as a concat() call.
|
||||||
|
"""
|
||||||
|
# Shortcut if at most a single quoting style is used
|
||||||
|
if "'" not in text or '"' not in text:
|
||||||
|
return repr(text)
|
||||||
|
parts = re.split('([\'"])', text)
|
||||||
|
# Python's repr() of strings will automatically choose the right quote
|
||||||
|
# type. Since each part only contains one "type" of quote, no escaping
|
||||||
|
# should be necessary.
|
||||||
|
parts = [repr(part) for part in parts if part]
|
||||||
|
return 'concat({})'.format(', '.join(parts))
|
||||||
|
|
||||||
|
|
||||||
@pytest.yield_fixture(scope='module')
|
@pytest.yield_fixture(scope='module')
|
||||||
def quteproc_process(qapp, httpbin, request):
|
def quteproc_process(qapp, httpbin, request):
|
||||||
|
189
tests/integration/test_dirbrowser.py
Normal file
189
tests/integration/test_dirbrowser.py
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||||
|
|
||||||
|
# Copyright 2015-2016 Daniel Schadt
|
||||||
|
#
|
||||||
|
# This file is part of qutebrowser.
|
||||||
|
#
|
||||||
|
# qutebrowser is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# qutebrowser is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
"""Test the built-in directory browser."""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import bs4
|
||||||
|
import collections
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QUrl
|
||||||
|
from qutebrowser.utils import jinja
|
||||||
|
|
||||||
|
|
||||||
|
class DirLayout:
|
||||||
|
|
||||||
|
"""Provide a fake directory layout to test dirbrowser."""
|
||||||
|
|
||||||
|
LAYOUT = [
|
||||||
|
'folder0/file00',
|
||||||
|
'folder0/file01',
|
||||||
|
'folder1/folder10/file100',
|
||||||
|
'folder1/file10',
|
||||||
|
'folder1/file11',
|
||||||
|
'file0',
|
||||||
|
'file1',
|
||||||
|
]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def layout_folders(cls):
|
||||||
|
"""Return all folders in the root directory of the layout."""
|
||||||
|
folders = set()
|
||||||
|
for path in cls.LAYOUT:
|
||||||
|
parts = path.split('/')
|
||||||
|
if len(parts) > 1:
|
||||||
|
folders.add(parts[0])
|
||||||
|
folders = list(folders)
|
||||||
|
folders.sort()
|
||||||
|
return folders
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_folder_content(cls, name):
|
||||||
|
"""Return (folders, files) for the given folder in the root dir."""
|
||||||
|
folders = set()
|
||||||
|
files = set()
|
||||||
|
for path in cls.LAYOUT:
|
||||||
|
if not path.startswith(name + '/'):
|
||||||
|
continue
|
||||||
|
parts = path.split('/')
|
||||||
|
if len(parts) == 2:
|
||||||
|
files.add(parts[1])
|
||||||
|
else:
|
||||||
|
folders.add(parts[1])
|
||||||
|
folders = list(folders)
|
||||||
|
folders.sort()
|
||||||
|
files = list(files)
|
||||||
|
files.sort()
|
||||||
|
return (folders, files)
|
||||||
|
|
||||||
|
def __init__(self, factory):
|
||||||
|
self._factory = factory
|
||||||
|
self.base = factory.getbasetemp()
|
||||||
|
self.layout = factory.mktemp('layout')
|
||||||
|
self._mklayout()
|
||||||
|
|
||||||
|
def _mklayout(self):
|
||||||
|
for filename in self.LAYOUT:
|
||||||
|
self.layout.ensure(filename)
|
||||||
|
|
||||||
|
def file_url(self):
|
||||||
|
"""Return a file:// link to the directory."""
|
||||||
|
return jinja.file_url(str(self.layout))
|
||||||
|
|
||||||
|
def path(self, *parts):
|
||||||
|
"""Return the path to the given file inside the layout folder."""
|
||||||
|
return os.path.normpath(str(self.layout.join(*parts)))
|
||||||
|
|
||||||
|
def base_path(self):
|
||||||
|
"""Return the path of the base temporary folder."""
|
||||||
|
return os.path.normpath(str(self.base))
|
||||||
|
|
||||||
|
|
||||||
|
Parsed = collections.namedtuple('Parsed', 'path, parent, folders, files')
|
||||||
|
Item = collections.namedtuple('Item', 'path, link, text')
|
||||||
|
|
||||||
|
|
||||||
|
def parse(quteproc):
|
||||||
|
"""Parse the dirbrowser content from the given quteproc.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
quteproc: The quteproc fixture.
|
||||||
|
"""
|
||||||
|
html = quteproc.get_content(plain=False)
|
||||||
|
soup = bs4.BeautifulSoup(html, 'html.parser')
|
||||||
|
print(soup.prettify())
|
||||||
|
title_prefix = 'Browse directory: '
|
||||||
|
# Strip off the title prefix to obtain the path of the folder that
|
||||||
|
# we're browsing
|
||||||
|
path = soup.title.string[len(title_prefix):]
|
||||||
|
path = os.path.normpath(path)
|
||||||
|
|
||||||
|
container = soup('div', id='dirbrowserContainer')[0]
|
||||||
|
|
||||||
|
parent_elem = container('ul', class_='parent')
|
||||||
|
if not parent_elem:
|
||||||
|
parent = None
|
||||||
|
else:
|
||||||
|
parent = QUrl(parent_elem[0].li.a['href']).toLocalFile()
|
||||||
|
parent = os.path.normpath(parent)
|
||||||
|
|
||||||
|
folders = []
|
||||||
|
files = []
|
||||||
|
|
||||||
|
for css_class, list_ in [('folders', folders), ('files', files)]:
|
||||||
|
for li in container('ul', class_=css_class)[0]('li'):
|
||||||
|
item_path = QUrl(li.a['href']).toLocalFile()
|
||||||
|
item_path = os.path.normpath(item_path)
|
||||||
|
list_.append(Item(path=item_path, link=li.a['href'],
|
||||||
|
text=str(li.a.string)))
|
||||||
|
|
||||||
|
return Parsed(path=path, parent=parent, folders=folders,
|
||||||
|
files=files)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='module')
|
||||||
|
def dir_layout(tmpdir_factory):
|
||||||
|
return DirLayout(tmpdir_factory)
|
||||||
|
|
||||||
|
|
||||||
|
def test_parent_folder(dir_layout, quteproc):
|
||||||
|
quteproc.open_url(dir_layout.file_url())
|
||||||
|
page = parse(quteproc)
|
||||||
|
assert page.parent == dir_layout.base_path()
|
||||||
|
|
||||||
|
|
||||||
|
def test_parent_with_slash(dir_layout, quteproc):
|
||||||
|
"""Test the parent link with an URL that has a trailing slash."""
|
||||||
|
quteproc.open_url(dir_layout.file_url() + '/')
|
||||||
|
page = parse(quteproc)
|
||||||
|
assert page.parent == dir_layout.base_path()
|
||||||
|
|
||||||
|
|
||||||
|
def test_parent_in_root_dir(dir_layout, quteproc):
|
||||||
|
# This actually works on windows
|
||||||
|
root_path = os.path.realpath('/')
|
||||||
|
urlstr = QUrl.fromLocalFile(root_path).toString(QUrl.FullyEncoded)
|
||||||
|
quteproc.open_url(urlstr)
|
||||||
|
page = parse(quteproc)
|
||||||
|
assert page.parent is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_enter_folder_smoke(dir_layout, quteproc):
|
||||||
|
quteproc.open_url(dir_layout.file_url())
|
||||||
|
quteproc.send_cmd(':hint all normal')
|
||||||
|
# a is the parent link, s is the first listed folder/file
|
||||||
|
quteproc.send_cmd(':follow-hint s')
|
||||||
|
page = parse(quteproc)
|
||||||
|
assert page.path == dir_layout.path('folder0')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('folder', DirLayout.layout_folders())
|
||||||
|
def test_enter_folder(dir_layout, quteproc, folder):
|
||||||
|
quteproc.open_url(dir_layout.file_url())
|
||||||
|
quteproc.click_element(text=folder)
|
||||||
|
page = parse(quteproc)
|
||||||
|
assert page.path == dir_layout.path(folder)
|
||||||
|
assert page.parent == dir_layout.path()
|
||||||
|
folders, files = DirLayout.get_folder_content(folder)
|
||||||
|
foldernames = [item.text for item in page.folders]
|
||||||
|
assert foldernames == folders
|
||||||
|
filenames = [item.text for item in page.files]
|
||||||
|
assert filenames == files
|
@ -158,3 +158,40 @@ def test_log_line_parse(data, attrs):
|
|||||||
def test_log_line_no_match():
|
def test_log_line_no_match():
|
||||||
with pytest.raises(testprocess.InvalidLine):
|
with pytest.raises(testprocess.InvalidLine):
|
||||||
quteprocess.LogLine("Hello World!")
|
quteprocess.LogLine("Hello World!")
|
||||||
|
|
||||||
|
|
||||||
|
class TestClickElement:
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def open_page(self, quteproc):
|
||||||
|
quteproc.open_path('data/click_element.html')
|
||||||
|
quteproc.wait_for_load_finished('data/click_element.html')
|
||||||
|
|
||||||
|
def test_click_element(self, quteproc):
|
||||||
|
quteproc.click_element('Test Element')
|
||||||
|
quteproc.wait_for_js('click_element clicked')
|
||||||
|
|
||||||
|
def test_click_special_chars(self, quteproc):
|
||||||
|
quteproc.click_element('"Don\'t", he shouted')
|
||||||
|
quteproc.wait_for_js('click_element special chars')
|
||||||
|
|
||||||
|
def test_duplicate(self, quteproc):
|
||||||
|
with pytest.raises(ValueError) as excinfo:
|
||||||
|
quteproc.click_element('Duplicate')
|
||||||
|
assert 'not unique' in str(excinfo.value)
|
||||||
|
|
||||||
|
def test_nonexistent(self, quteproc):
|
||||||
|
with pytest.raises(ValueError) as excinfo:
|
||||||
|
quteproc.click_element('no element exists with this text')
|
||||||
|
assert 'No element' in str(excinfo.value)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('string, expected', [
|
||||||
|
('Test', "'Test'"),
|
||||||
|
("Don't", '"Don\'t"'),
|
||||||
|
# This is some serious string escaping madness
|
||||||
|
('"Don\'t", he said',
|
||||||
|
"concat('\"', 'Don', \"'\", 't', '\"', ', he said')"),
|
||||||
|
])
|
||||||
|
def test_xpath_escape(string, expected):
|
||||||
|
assert quteprocess._xpath_escape(string) == expected
|
||||||
|
@ -28,6 +28,7 @@ from PyQt5.QtCore import QUrl
|
|||||||
from PyQt5.QtNetwork import QNetworkRequest
|
from PyQt5.QtNetwork import QNetworkRequest
|
||||||
|
|
||||||
from qutebrowser.browser.network import filescheme
|
from qutebrowser.browser.network import filescheme
|
||||||
|
from qutebrowser.utils import jinja
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('create_file, create_dir, filterfunc, expected', [
|
@pytest.mark.parametrize('create_file, create_dir, filterfunc, expected', [
|
||||||
@ -57,6 +58,8 @@ class TestIsRoot:
|
|||||||
|
|
||||||
@pytest.mark.windows
|
@pytest.mark.windows
|
||||||
@pytest.mark.parametrize('directory, is_root', [
|
@pytest.mark.parametrize('directory, is_root', [
|
||||||
|
('C:\\foo\\bar', False),
|
||||||
|
('C:\\foo\\', False),
|
||||||
('C:\\foo', False),
|
('C:\\foo', False),
|
||||||
('C:\\', True)
|
('C:\\', True)
|
||||||
])
|
])
|
||||||
@ -65,6 +68,8 @@ class TestIsRoot:
|
|||||||
|
|
||||||
@pytest.mark.posix
|
@pytest.mark.posix
|
||||||
@pytest.mark.parametrize('directory, is_root', [
|
@pytest.mark.parametrize('directory, is_root', [
|
||||||
|
('/foo/bar', False),
|
||||||
|
('/foo/', False),
|
||||||
('/foo', False),
|
('/foo', False),
|
||||||
('/', True)
|
('/', True)
|
||||||
])
|
])
|
||||||
@ -72,6 +77,38 @@ class TestIsRoot:
|
|||||||
assert filescheme.is_root(directory) == is_root
|
assert filescheme.is_root(directory) == is_root
|
||||||
|
|
||||||
|
|
||||||
|
class TestParentDir:
|
||||||
|
|
||||||
|
@pytest.mark.windows
|
||||||
|
@pytest.mark.parametrize('directory, parent', [
|
||||||
|
('C:\\foo\\bar', 'C:\\foo'),
|
||||||
|
('C:\\foo', 'C:\\'),
|
||||||
|
('C:\\foo\\', 'C:\\'),
|
||||||
|
('C:\\', 'C:\\'),
|
||||||
|
])
|
||||||
|
def test_windows(self, directory, parent):
|
||||||
|
assert filescheme.parent_dir(directory) == parent
|
||||||
|
|
||||||
|
@pytest.mark.posix
|
||||||
|
@pytest.mark.parametrize('directory, parent', [
|
||||||
|
('/home/foo', '/home'),
|
||||||
|
('/home', '/'),
|
||||||
|
('/home/', '/'),
|
||||||
|
('/', '/'),
|
||||||
|
])
|
||||||
|
def test_posix(self, directory, parent):
|
||||||
|
assert filescheme.parent_dir(directory) == parent
|
||||||
|
|
||||||
|
|
||||||
|
def _file_url(path):
|
||||||
|
"""Return a file:// url (as string) for the given LocalPath.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
path: The filepath as LocalPath (as handled by py.path)
|
||||||
|
"""
|
||||||
|
return jinja.file_url(str(path))
|
||||||
|
|
||||||
|
|
||||||
class TestDirbrowserHtml:
|
class TestDirbrowserHtml:
|
||||||
|
|
||||||
Parsed = collections.namedtuple('Parsed', 'parent, folders, files')
|
Parsed = collections.namedtuple('Parsed', 'parent, folders, files')
|
||||||
@ -87,7 +124,7 @@ class TestDirbrowserHtml:
|
|||||||
container = soup('div', id='dirbrowserContainer')[0]
|
container = soup('div', id='dirbrowserContainer')[0]
|
||||||
|
|
||||||
parent_elem = container('ul', class_='parent')
|
parent_elem = container('ul', class_='parent')
|
||||||
if len(parent_elem) == 0:
|
if not parent_elem:
|
||||||
parent = None
|
parent = None
|
||||||
else:
|
else:
|
||||||
parent = parent_elem[0].li.a.string
|
parent = parent_elem[0].li.a.string
|
||||||
@ -145,8 +182,8 @@ class TestDirbrowserHtml:
|
|||||||
parsed = parser(str(tmpdir))
|
parsed = parser(str(tmpdir))
|
||||||
assert parsed.parent
|
assert parsed.parent
|
||||||
assert not parsed.folders
|
assert not parsed.folders
|
||||||
foo_item = self.Item('file://' + str(foo_file), foo_file.relto(tmpdir))
|
foo_item = self.Item(_file_url(foo_file), foo_file.relto(tmpdir))
|
||||||
bar_item = self.Item('file://' + str(bar_file), bar_file.relto(tmpdir))
|
bar_item = self.Item(_file_url(bar_file), bar_file.relto(tmpdir))
|
||||||
assert parsed.files == [bar_item, foo_item]
|
assert parsed.files == [bar_item, foo_item]
|
||||||
|
|
||||||
def test_html_special_chars(self, tmpdir, parser):
|
def test_html_special_chars(self, tmpdir, parser):
|
||||||
@ -154,8 +191,7 @@ class TestDirbrowserHtml:
|
|||||||
special_file.ensure()
|
special_file.ensure()
|
||||||
|
|
||||||
parsed = parser(str(tmpdir))
|
parsed = parser(str(tmpdir))
|
||||||
item = self.Item('file://' + str(special_file),
|
item = self.Item(_file_url(special_file), special_file.relto(tmpdir))
|
||||||
special_file.relto(tmpdir))
|
|
||||||
assert parsed.files == [item]
|
assert parsed.files == [item]
|
||||||
|
|
||||||
def test_dirs(self, tmpdir, parser):
|
def test_dirs(self, tmpdir, parser):
|
||||||
@ -167,8 +203,8 @@ class TestDirbrowserHtml:
|
|||||||
parsed = parser(str(tmpdir))
|
parsed = parser(str(tmpdir))
|
||||||
assert parsed.parent
|
assert parsed.parent
|
||||||
assert not parsed.files
|
assert not parsed.files
|
||||||
foo_item = self.Item('file://' + str(foo_dir), foo_dir.relto(tmpdir))
|
foo_item = self.Item(_file_url(foo_dir), foo_dir.relto(tmpdir))
|
||||||
bar_item = self.Item('file://' + str(bar_dir), bar_dir.relto(tmpdir))
|
bar_item = self.Item(_file_url(bar_dir), bar_dir.relto(tmpdir))
|
||||||
assert parsed.folders == [bar_item, foo_item]
|
assert parsed.folders == [bar_item, foo_item]
|
||||||
|
|
||||||
def test_mixed(self, tmpdir, parser):
|
def test_mixed(self, tmpdir, parser):
|
||||||
@ -178,8 +214,8 @@ class TestDirbrowserHtml:
|
|||||||
bar_dir.ensure(dir=True)
|
bar_dir.ensure(dir=True)
|
||||||
|
|
||||||
parsed = parser(str(tmpdir))
|
parsed = parser(str(tmpdir))
|
||||||
foo_item = self.Item('file://' + str(foo_file), foo_file.relto(tmpdir))
|
foo_item = self.Item(_file_url(foo_file), foo_file.relto(tmpdir))
|
||||||
bar_item = self.Item('file://' + str(bar_dir), bar_dir.relto(tmpdir))
|
bar_item = self.Item(_file_url(bar_dir), bar_dir.relto(tmpdir))
|
||||||
assert parsed.parent
|
assert parsed.parent
|
||||||
assert parsed.files == [foo_item]
|
assert parsed.files == [foo_item]
|
||||||
assert parsed.folders == [bar_item]
|
assert parsed.folders == [bar_item]
|
||||||
|
Loading…
Reference in New Issue
Block a user