Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 65 additions & 68 deletions src/paste/templates/paste.html
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@
.copy-buttons-container {
position: absolute;
right: 20px;
top: 20px;
top: 70px;
display: flex;
flex-direction: column;
gap: 10px;
Expand Down Expand Up @@ -305,7 +305,7 @@

.copy-buttons-container {
right: 10px;
top: 10px;
top: 60px;
}

.copy-button {
Expand All @@ -331,16 +331,15 @@
<a href="{{ url_for('indexpage') }}" class="header-link">
<div class="glitch-text" data-text="paste.py 🐍"><u>paste.py</u> 🐍</div>
</a>
<div style="width: 50px;"></div>
<div class="copy-buttons-container">
<button id="copyButton" class="copy-button" onclick="copyAllText()">
<i class="fas fa-copy"></i> COPY CODE
</button>
<button id="copyLinkButton" class="copy-button" onclick="copyLink()">
<i class="fas fa-link"></i> COPY LINK
</button>
</div>
</div>
<div class="copy-buttons-container">
<button id="copyButton" class="copy-button" onclick="copyAllText()">
<i class="fas fa-copy"></i> COPY CODE
</button>
<button id="copyLinkButton" class="copy-button" onclick="copyLink()">
<i class="fas fa-link"></i> COPY LINK
</button>
</div>
<div class="code">
{{ highlighted_code | safe }}
</div>
Expand Down Expand Up @@ -402,77 +401,75 @@
}, 300);
});

function copyAllText() {
const codeElement = document.querySelector('.code pre');
const range = document.createRange();
range.selectNode(codeElement);

const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);

document.execCommand('copy');
selection.removeAllRanges();

const copyButton = document.getElementById('copyButton');
const originalText = copyButton.innerHTML;
copyButton.innerHTML = '<i class="fas fa-check"></i> COPIED!';

function showCopyFeedback(button, success, successHTML, failureHTML) {
const originalText = button.innerHTML;
button.innerHTML = success ? successHTML : failureHTML;
setTimeout(() => {
copyButton.innerHTML = originalText;
button.innerHTML = originalText;
}, 2000);
}

function copyLink() {
const currentUrl = window.location.href;

if (navigator.clipboard) {
navigator.clipboard.writeText(currentUrl).then(() => {
const copyLinkButton = document.getElementById('copyLinkButton');
const originalText = copyLinkButton.innerHTML;
copyLinkButton.innerHTML = '<i class="fas fa-check"></i> LINK COPIED!';

setTimeout(() => {
copyLinkButton.innerHTML = originalText;
}, 2000);
}).catch(() => {
fallbackCopyLink(currentUrl);
});
} else {
fallbackCopyLink(currentUrl);
}
}

function fallbackCopyLink(text) {

function fallbackCopy(textToCopy, button, successHTML, failureHTML) {
const textArea = document.createElement("textarea");
textArea.value = text;
textArea.value = textToCopy;
textArea.style.position = "fixed";
textArea.style.left = "-999999px";
textArea.style.top = "-999999px";
document.body.appendChild(textArea);
textArea.focus();
textArea.select();


let success = false;
try {
document.execCommand('copy');
const copyLinkButton = document.getElementById('copyLinkButton');
const originalText = copyLinkButton.innerHTML;
copyLinkButton.innerHTML = '<i class="fas fa-check"></i> LINK COPIED!';

setTimeout(() => {
copyLinkButton.innerHTML = originalText;
}, 2000);
success = document.execCommand('copy');
} catch (err) {
const copyLinkButton = document.getElementById('copyLinkButton');
const originalText = copyLinkButton.innerHTML;
copyLinkButton.innerHTML = '<i class="fas fa-times"></i> COPY FAILED';

setTimeout(() => {
copyLinkButton.innerHTML = originalText;
}, 2000);
console.error('Fallback copy failed', err);
}

document.body.removeChild(textArea);
showCopyFeedback(button, success, successHTML, failureHTML);
}

function copyAllText() {
const codeElement = document.querySelector('.code pre');
if (!codeElement) return;

const clone = codeElement.cloneNode(true);
const lineNumbers = clone.querySelectorAll('.linenos');
lineNumbers.forEach(span => span.remove());
const textToCopy = clone.textContent;

const copyButton = document.getElementById('copyButton');
const successHTML = '<i class="fas fa-check"></i> COPIED!';
const failureHTML = '<i class="fas fa-times"></i> FAILED!';

if (navigator.clipboard) {
navigator.clipboard.writeText(textToCopy).then(() => {
showCopyFeedback(copyButton, true, successHTML, failureHTML);
}).catch(err => {
console.error('Async copy failed, using fallback', err);
fallbackCopy(textToCopy, copyButton, successHTML, failureHTML);
});
} else {
fallbackCopy(textToCopy, copyButton, successHTML, failureHTML);
}
}

function copyLink() {
const currentUrl = window.location.href;
const copyLinkButton = document.getElementById('copyLinkButton');
const successHTML = '<i class="fas fa-check"></i> LINK COPIED!';
const failureHTML = '<i class="fas fa-times"></i> COPY FAILED';

if (navigator.clipboard) {
navigator.clipboard.writeText(currentUrl).then(() => {
showCopyFeedback(copyLinkButton, true, successHTML, failureHTML);
}).catch(() => {
fallbackCopy(currentUrl, copyLinkButton, successHTML, failureHTML);
});
} else {
fallbackCopy(currentUrl, copyLinkButton, successHTML, failureHTML);
}
}

{% endblock %}
129 changes: 11 additions & 118 deletions tests/test_api.py
Original file line number Diff line number Diff line change
@@ -1,133 +1,26 @@
from fastapi.testclient import TestClient
from src.paste.main import app
from typing import Optional
import os

client: TestClient = TestClient(app)

file: Optional[str] = None
paste_id: Optional[str] = None


def test_get_health_route() -> None:
data: dict = {"status": "ok"}
response = client.get("/health")
assert response.status_code == 200
assert response.json() == data


def test_get_homepage_route() -> None:
response_expected_headers: str = "text/html; charset=utf-8"
response = client.get("/")
assert response.status_code == 200
assert response.headers.get("Content-Type", "") == response_expected_headers


def test_get_web_route() -> None:
response_expected_headers: str = "text/html; charset=utf-8"
response = client.get("/web")
assert response.status_code == 200
assert response.headers.get("Content-Type", "") == response_expected_headers


def test_get_paste_data_route() -> None:
data: str = "This is a test file."
response = client.get("/paste/test")
assert response.status_code == 200
assert data in response.text


def test_post_web_route() -> None:
data: str = "This is a test data"
form_data: dict = {"content": data, "extension": ".txt"}
response = client.post("/web", data=form_data)
global file
file = str(response.url).split("/")[-1]
assert response.status_code == 200
assert data in response.text


def test_delete_paste_route() -> None:
expected_response: str = f"File successfully deleted {file}"
response = client.delete(f"/paste/{file}")
assert response.status_code == 200
assert response.text == expected_response


def test_post_file_route() -> None:
response = client.post("/file", files={"file": ("test.txt", b"test file content")})
assert response.status_code == 201
response_file_uuid: str = response.text
response = client.get(f"/paste/{response_file_uuid}")
assert response.status_code == 200
assert "test file content" in response.text
response = client.delete(f"/paste/{response_file_uuid}")
assert response.status_code == 200
assert f"File successfully deleted {response_file_uuid}" in response.text


def test_post_file_route_failure() -> None:
response = client.post("/file")
assert response.status_code == 422 # Unprocessable Entity
# Add body assertion in future.


def test_post_file_route_size_limit() -> None:
large_file_name: str = "large_file.txt"
file_size: int = 20 * 1024 * 1024 # 20 MB in bytes
additional_bytes: int = 100 # Adding some extra bytes to exceed 20 MB
content: bytes = b"This is a line in the file.\n"
with open(large_file_name, "wb") as file:
while file.tell() < file_size:
file.write(content)
file.write(b"Extra bytes to exceed 20 MB\n" * additional_bytes)
file.close()
f = open(large_file_name, "rb")
files: dict = {"file": f}
response = client.post("/file", files=files)
f.close()
# cleanup
os.remove(large_file_name)
assert response.status_code == 413
assert "File is too large" in response.text


def test_post_api_paste_route() -> None:
paste_data = {"content": "This is a test paste content", "extension": "txt"}
response = client.post("/api/paste", json=paste_data)
assert response.status_code == 201
response_json = response.json()
assert "uuid" in response_json
assert "url" in response_json
assert response_json["uuid"].endswith(".txt")
assert response_json["url"].startswith("http://paste.fosscu.org/paste/")

# Clean up: delete the created paste
uuid = response_json["uuid"]
delete_response = client.delete(f"/paste/{uuid}")
assert delete_response.status_code == 200


def test_get_api_paste_route() -> None:
# First, create a paste
paste_data = {"content": "This is a test paste content for GET", "extension": "md"}
create_response = client.post("/api/paste", json=paste_data)
assert create_response.status_code == 201
created_uuid = create_response.json()["uuid"]

# Now, test getting the paste
response = client.get(f"/api/paste/{created_uuid}")
assert response.status_code == 200
response_json = response.json()
assert response_json["uuid"] == created_uuid
assert response_json["content"] == paste_data["content"]
assert response_json["extension"] == paste_data["extension"]

# Clean up: delete the created paste
delete_response = client.delete(f"/paste/{created_uuid}")
assert delete_response.status_code == 200
def test_paste_api_route() -> None:
respose = client.post(
"/api/paste",
json={
"content": "Hello-World",
},
)
paste_id = respose.text
assert respose.status_code == 201


def test_get_api_paste_route_not_found() -> None:
response = client.get("/api/paste/nonexistent_uuid.txt")
assert response.status_code == 404
assert response.json()["detail"] == "Paste not found"
print(paste_id)